PANGGUNG BAYANG
← back to the screen
DAINER.AI · concept study № 013 · the method

How the shadows were made

One operator wrote the brief and set the constraints. AI agents built the house, then tore it apart three times over. This page keeps the working notes: the instruction it was given, the one trick it had to prove, and every fault the critique caught on the way to the light.

The brief it received

A shadow-puppet house, and one thing to prove

Every site in this collection starts from a place with a premise, not a page of sections. This one was handed to the builder verbatim:

Name
PANGGUNG BAYANG — a shadow-puppet house, Kelantan lineage, est. 1961
Concept
The screen is lit, the dalang is gone — you drag the jointed puppets behind the muslin and the oil lamp throws their story for you.
Palette
muslin glow #f4e3c2 · lamp amber #e8a33d · shadow black #100c08 · blood red #a3282e
Type
Cinzel (display) + Cabin (text)
Signature
Jointed shadow-puppet physics — verlet/IK limbs on draggable puppets rendered as true shadows on a lit screen; the lamp flickers, shadows breathe.
Mood
mythic, flickering, reverent · the dalang's grandson, keeping the light on
The signature technique

Bones that swing, ink that stretches

The puppets are not sprites. Each figure carries two arms of two bones each. Every frame, the shoulder is pinned to the leaning body and the elbow and hand are integrated as free points, then the bone lengths are re-solved eight times so the arm can whip and settle but never snap. It is the same verlet trick a rope uses, cut down to a limb:

// integrate the free joints, then solve the bones (from index.html)
for(const pt of [arm.e,arm.h]){
  let vX=(pt.x-pt.ox)*.88, vY=(pt.y-pt.oy)*.88;
  pt.ox=pt.x;pt.oy=pt.y;pt.x+=vX-accel*1.4;pt.y+=vY+grav;
}
for(let i=0;i<8;i++){
  constrain(s,arm.e,T.l1,true);   // shoulder→elbow, shoulder pinned
  constrain(arm.e,arm.h,T.l2,false); // elbow→hand, both float
}
The accel term is the body's own motion — drag a figure fast and its arms trail behind, exactly like a puppet worked by a rod.

Nothing is drawn in its own colour. Every limb is painted as a single dark shape and then sheared and stretched away from the lamp — the further a figure stands from the flame, the longer and thinner its shadow throws. Move the lamp and the whole cast leans:

// cast the shadow: lean away from the lamp, stretch with distance
let k = clamp(-dx / max(240, |dy|+220), -0.4, 0.4);
const stretch = 1 + min(0.5, dist/(H*2.2));
ctx.transform(1, 0, k, stretch, 0, 0);
One shared oil-lamp position feeds the muslin's radial light, every figure's lean, and the drifting motes — a single light source, honestly obeyed.
The critique loop

What each pass caught

The first draft is never the reason these pages look the way they do. The loop is: shoot the real page on desktop and mobile, read the screenshots like a hostile art director, fix everything, then add one deliberate upgrade. Three times.

Pass 1 — the build

A lit stage that fought its own words

  • Stood up the lit muslin, the jointed cast, the flickering pelita, the backstage and the register.
  • Caught: the two figures leaned so hard they fell across the headline and the opening paragraph — dark leather on dark type, both illegible.
  • Caught: the footer bragged "zero CDN" while quietly loading fonts from Google — a false stat.
  • Caught: the backstage flame floated alone in a black void, and the footer's right half was empty.
Pass 2 — composition + life

Move the cast off the copy; make the dark breathe

  • Walked the opening cast to the right of the text and halved the shear so figures stand instead of topple; made the puppet scale width-aware so on a phone they sit below the copy, not through it.
  • Replaced the false line with an honest one — real weight, "0 JS libraries" — and built a colophon plaque (register, kelir, figures, lineage) to fill the footer void.
  • Upgrade: gave every dark screenful a breathing lamp-seep, the cast cards a slow light-sweep, and the register a lamp-flicker — nothing on the page sits perfectly still.
Pass 3 — polish + proof

Trim the crowns, clear the kicker, prove it

  • Eased the cast-card scale so the demon-king's tall crown stops clipping its frame; nudged the phone lamp off the corner so it no longer sits on the kicker line.
  • Verified at 375 / 768 / 1440, confirmed keyboard nudges and visible focus on every handle, and checked the reduced-motion page is still a complete, readable house with the flame held still.
  • Ship gate: zero console errors on both viewports; weight printed in the footer.