Radio Merdeka · build notes

Concept study № 18 · how it was made

The set was built to prove one thing.

That a browser can synthesize a whole radio band — carriers, stations and the real static between them — from oscillators and filtered noise, and never touch a recording. Below: the brief, the one technique that carries it, and the three critique passes that took it from a working prototype to something worth keeping the light on for.

What we were handed

The brief

Every site in this collection begins as an art-directed brief — a fictional place with a year, a palette, a named type pairing and exactly one signature technique to execute deeply. Radio Merdeka received this:

NameRadio Merdeka — broadcast archive, 1957
ConceptA bakelite radio tuned across independence year — the dial drifts through synthesized stations, and the static between them is real.
Palettebakelite #241812 · dial glow #f0b64a · static cream #e8ddc8 · tube orange #d96b2f
TypePoiret One (display) + Work Sans (text)
SignatureA WebAudio tuning dial — oscillator-built carriers, filtered-noise static, station voices as synthesized tone patterns; an oscilloscope draws it all. Audio law: silent until the visitor turns the dial; a visible mute; no autoplay.
InteractionsDrag the dial (+ arrow keys) · signal-strength needle · save a frequency as a preset · one frequency broadcasts only at night
Mood · voiceAnalog, golden, patriotic-soft · a continuity announcer, measured

One technique, executed deeply

The band is computed, not recorded

The whole dial is a single mixing problem. For any frequency, the set finds the nearest station and computes a signal strength — 1 when you are dead on it, falling to 0 as you drift a few kilohertz away. That one number cross-fades the station's synthesized voice against a bed of filtered white noise. Lock a station and its voice fills the room; drift, and the noise rises to take its place. Nothing is looped. The hiss you hear was generated the moment you asked for it.

Each station's "voice" is a scheduled pattern of oscillator notes — time pips, a plucked kroncong, a telegraph key still spelling merdeka, bronze gamelan, a night service that keeps announcer's hours. Every note runs through the same tiny envelope helper:

// one oscillator note, scheduled into the active station's bus
function tone(t,dur,f,type,peak,a,mod){
  var o=ctx.createOscillator(); o.type=type; o.frequency.value=f;
  var g=ctx.createGain();
  g.gain.setValueAtTime(0.0001,t);
  g.gain.linearRampToValueAtTime(peak, t+(a||0.008));
  g.gain.exponentialRampToValueAtTime(0.0001, t+dur);
  o.connect(g); g.connect(stationBus);
  // …optional FM modulator for the gamelan bells…
  o.start(t); o.stop(t+dur+0.06);
}

// the whole dial in two lines — strength cross-fades voice against static
staticGain.gain.setTargetAtTime((1-curStrength)*0.085+0.004, now, 0.08);
stationBus.gain.setTargetAtTime(active ? curStrength*0.55 : 0, now, 0.05);
Excerpt from index.html — the note synthesizer and the static/signal mix. The oscilloscope draws the same waveform it plays, so the page stays fully legible with the sound off.

The audio context is never created until your first tune gesture — turning the knob, dragging the glass, an arrow key. Until then the oscilloscope runs on a purely visual version of the same maths, so the instrument is alive and understandable in complete silence. That is the audio law, kept honestly.

The loop is the level

Three critique passes

The first draft is never why these pages look the way they do. Each pass shoots the real page on desktop and mobile, reads the screenshots like a hostile art director, fixes everything, and ends with one deliberate upgrade.

Pass 01 · first light

The dial fought its own edges

Found: the seven station names printed on the glass clipped at the band edges — "Siaran Nasional" opened as "…an Nasional" — and collided with a "kHz · medium wave" caption. On mobile all seven names overlapped into an unreadable smear. The plate was cramped on narrow screens, and the set opened on dead static, which read as broken.

Changed: inset every dial element by 4.5% so nothing touches the glass; removed the caption; hid the printed names on mobile (they live in the station log); collapsed the plate on small screens; and opened the radio already locked on Kroncong Senja at 855 kHz, so the first frame shows a working instrument.

Upgrade: signal-lock jade halo + readout turns green on lock
Pass 02 · the deck

Every control had to answer the thumb

Found: the fixes held, but the lower deck — tuning knob, oscilloscope, signal meter, volume, mute, five presets — needed to survive the drop to a single mobile column without any control leaving thumb reach, and the oscilloscope trace read as one flat colour whether tuned or lost.

Changed: confirmed the 3-then-2 preset grid and reachable knobs at 390px, tidied the volume handler, and verified keyboard paths and visible focus on every knob, preset and button.

Upgrade: the trace colour-grades with strength — dim amber static → bright gold carrier
Pass 03 · the warm detail

A 1957 set has an eye

Found: the instrument was correct but missing the single most period-accurate detail a valve radio of this era carried — the magic-eye tuning indicator that told you, at a glance, when you were on station.

Changed: added an EM34-style magic eye to the brand plate. Its green phosphor wedges close as the signal locks and dim to nothing when the set is off — computed from the same strength value that drives the audio, so the whole face agrees with itself.

Upgrade: the magic-eye tuning indicator

Ship gate

What it costs

  • One self-contained index.html — all CSS and JS inline, zero CDN, no build step.
  • No external images — the cabinet, dial, knobs, oscilloscope and static are all procedural.
  • Zero console errors on the final harness run, at 390px and 1440px.
  • Full keyboard operability, visible focus, AA-contrast text, and a complete page under prefers-reduced-motion.
  • Page weight printed on the radio's own footer — the number is the proof.

The method that seeded this collection is the open FABLE 25 project — a design constitution, art-directed briefs, and a screenshot critique loop. We rebuilt the discipline in our own voice; the craft here is real, the brand is fiction.