The making of № 19 · HURUF
Nota kerja.
HURUF is a fictional Malay type foundry. Its one real trick is physical: a variable font wired to a spring solver, so weight is not a number you type — it is a force you swing, and the letters overshoot and settle like they have mass. This is the work log: the brief, the physics, and everything three critique passes tore out.
01 · The brief we were handed
A foundry where the alphabet is alive.
Verbatim from the wave-3 brief (W3-19). The build follows its concept, palette, type and signature exactly — no drift.
- Concept
- Grab the weight axis and swing it; letters resist, snap and follow like they have mass.
- Palette
- ultra black #0a0a0a · hot coral #ff6b4a · bone #e8e4da
- Type
- Roboto Serif (variable display, wght 100–900 · wdth 75–150) + Instrument Sans (text)
- Signature
- Variable-font kinetic playground — drag axes and physics-spring the glyphs; type your own word and it inherits the motion; the specimen wall responds to the cursor.
- Interactions
- Axis drag with spring physics · type-your-word specimen · cursor-magnet letters · a hidden pangram in Bahasa
- Mood
- Loud, precise, confident
- Voice
- Foundry manifesto — short declaratives
02 · The signature technique
One spring, driving font-variation-settings.
Every animated glyph holds a tiny spring: a value chasing a target with velocity and damping. Each frame the solver integrates one step, and the result is written straight into the font's wght axis. Point the target at where your hand is — the crown, a slider, or the cursor's distance to a letter — and the type accelerates toward it, overshoots, and rings down. That overshoot is the whole illusion of mass.
// the entire physics engine — a damped spring, integrated per frame function step(s, stiff, damp, dt){ var a = -stiff*(s.v - s.t) - damp*s.vel; // pull toward target, minus drag s.vel += a*dt; s.v += s.vel*dt; // integrate velocity, then position return s.v; } // per hero letter, every frame: target = live weight + proximity to the cursor s.w.t = clamp(w + boost + breath, 80, 940); var wv = step(s.w, 120, 13, dt); // underdamped → it overshoots s.el.style.fontVariationSettings = "'wght' "+wv.toFixed(0)+",'wdth' "+xr;
The same solver runs the hero word, the 26-letter specimen wall (each cell a coupled spring on its own idle phase), the draggable crown, and the two console sliders — one loop, paused when the tab is hidden, capped at devicePixelRatio 2. With motion turned off, a separate static path keeps every control working; the springs just settle instantly.
03 · What the critique passes found
The loop is the level.
Each pass: shoot the real page on desktop and mobile, read the pixels like a hostile art director, fix everything, then add one deliberate upgrade.
The wall had collapsed — and the crown was smiling
- The specimen wall shared an id with its section, so all 26 glyphs were injected after the pangram and stacked in a single dead column. Renamed the grid; the A–Z band snapped into place.
- The crown's grip mark was drawn as a rotated arc — on screen it read as a smiley face. Redrawn as a two-bar drag handle.
- Upgrade: a cursor gravity-well — a soft acid light that follows the hand, so the page feels lit by the thing the letters lean toward.
A pronoun that lied, and a range you couldn't see
- The Malay line opened on the pronoun “Ia”, but Instrument Sans draws a capital I as a bare stem — it read as “la”. Rewritten to “Berat itu daya, bukan nombor.”
- The hero showed one weight at a time; you couldn't feel the axis it travels. Added an axis ruler (100 → 900) under the crown.
- Upgrade: the weight envelope — a faint acid wireframe of the word at its heaviest, haloing behind the live letters, so you see the full range the alphabet fights across.
Motion off broke the instrument; the halo broke the phone
- Under prefers-reduced-motion the animation loop never started, so the sliders moved nothing. Added a static-paint path — controls still set the type, they just don't spring.
- The centered envelope pushed 28px of horizontal overflow on a 390px screen. Clamped with overflow-x:clip and softened the halo on small viewports.
- Upgrade: verified the crown by keyboard — fourteen ArrowRights wind it to 900 and trip the hidden Bahasa pangram, exactly like the drag does.
Resume pass — a broken root tag and a dead band
- A stray, self-closed <html> tag sat mid-head, so the document language never bound to the root. Moved one valid <html lang> to the top and deleted the ghost — on both the site and this guide.
- The foundry-facts row and the space above the sign-off sat motionless — a static screenful is a defect here. The stat figures now wind up from zero on reveal, like a weight dial finding its number.
- Upgrade: a full-bleed specimen marquee between the catalogue and the sign-off. It drifts, and its weight is wired to the live crown — swing the axis and the whole running line thickens at once.
04 · What it proves
Type can be an instrument.
A variable font is usually a menu of weights. Wired to a solver it becomes a playable object — proof that a website can hand a business something tactile, not a slideshow. For a foundry that is the pitch; for a clinic or a lender it is the same engine pointed at a booking flow.