← all posts
September 17, 2026·Ronald

Ettaala: walking one lamp through an isometric house, room by room

The premise is Kevin Ngo's again. What I brought is a Ntinda house in cutaway, a camera baked into the projection instead of the canvas, and four separate discoveries that a knockout is not a lighting effect — it is occlusion, and it will erase your entire family.

9shots in 42 seconds
4.5 msmean frame cost
3render passes
91 KBone index.html
AnimationCanvasIsometricRisographClaude CodeUganda

The ⁠premise ​is ‌not ​mine. ​A ​second ​time: ‌the ​idea ‌of ​an ‌isometric ​riso ‌world of ​cutaway ​rooms, ‌and ‌of ‌a ​film ​that ​walks ​a ‌single ​light ​through ​them ‌one ‌room at ‌a ‌time, ​is ​Kevin ‌Ngo's ​(@kevin_t_ngo) ‌— ‌his piece ‌"a ‌small ​light, ‌room ⁠by ⁠room", ​also ‌made ​with ​Claude ​Code. ​The ‌workflow I ​followed ‌(plan ​the ‌rooms, ​block ‌them ​out, ​screenshot, ‌dress, ‌only ‌then animate) ​is ​the ​one ​published ‌as ​the riso-rooms Claude Code skill, which ​credits ​the ‌same ‌original.

No ‌code ‌came ​from ​either. ‌The ​engine ‌underneath ‌is ‌the ‌one ​written ‌for Nsenene Season ⁠last ⁠week ​— ‌the ​nine-ink ​riso compositor, ​the ​registration-aware ‌knockout, ​the ‌halftone ​screens, ‌the amadinda. ​This ‌post ​is ​about ‌the ‌layer ‌on ​top ​of ​it, ​which ‌is ​new: ​an isometric ​renderer, ‌a ‌camera ‌rig, ‌and ​a ​lighting ‌model ​in ‌which ‌a ‌lit ‌room ​is the ‌night ⁠ink ⁠removed.

The live page. Space to play, arrow keys to step a frame, ?t=16.5 to open paused inside the kitchen. Sound is off until you press it. 960x720, drawn entirely in code.

Ettaala ​is ‌Luganda ​for ​the ​lamp. ​The ‌house ​is ‌a ​three-storey ‌Ntinda ​plot: ‌a duka ​and ​its ‌store ‌on ‌the ​ground, ​a ​kitchen ​and ‌a ​sitting ​room ​above ‌it, ‌a bedroom ‌and ‌a ​balcony ​above ‌that, ​and ‌a ‌mabati ‌roof ‌with ​a ‌water ⁠tank, ⁠a washing ​line ‌and ​room ​to ​sit. ​Dusk ‌falls, ​the ‌shop ​light ‌comes ​on, ‌and ​the light ​climbs ‌the ‌building ‌ahead ​of ​the ​family ​until ‌the ​last ​window ​on ‌the ‌hill is ‌one ‌of ​thirty.

An isometric risograph drawing of a three-storey Kampala house at dusk, with an orange sky, hills, neighbouring houses and a mango tree
t = 2.5s. The establishing shot: the plot at dusk, the murram, the mango tree, the boda, and the neighbours' houses, which are only drawn at all when the camera is wide enough to see them.
An isometric risograph shop interior at night with shelves of goods, an orange counter, two dark figures and a pink poster
t = 9.0s. The duka. The shot the whole grammar was tuned on: goods legible at thumbnail scale, two figures dark against a lit floor, the light falling off across it, and a pink airtime poster doing what a pink airtime poster does in every shop in Kampala.

The camera goes in the projection, not on the canvas

The ​obvious ‌way ​to ‌move ‌a ‌camera ‌over ​a ‌canvas ⁠scene ⁠is ​ctx.translate ‌and ctx.scale. ​It ​is ​wrong ​here, ‌and ​the ‌reason ​is ‌the ​print.

Every ‌ink ​in ​this ‌engine ‌is ‌filled ​with ​a ​halftone ​pattern ‌set ​in ​canvas space. ​That ‌is ‌what ‌makes ‌the ​frame ​look ‌printed: ​the ‌dot ‌screen ‌belongs ‌to the ​sheet, ‌not ⁠to ⁠the ​drawing. ‌A ​canvas-space ​camera ​transform ​drags ‌the pattern ​along ‌with ​the ‌buildings ​— ‌the ​dots ​slide ‌across ‌the ‌picture ​as ​the crane ​moves ​and ‌the ​whole ​image ​swims ‌like ‌a ‌badly-keyed ‌video ​texture.

So ​the ‌camera ​lives ‌inside ‌the ‌projection ‌function ​instead:

js
const ISO_COS = Math.cos(Math.PI / 6), ISO_SIN = Math.sin(Math.PI / 6);
const CAM = { x: 0, y: 0, z: 0, zoom: 1 };

function P(x, y, z) {
  const sx = (x - y) * ISO_COS, sy = (x + y) * ISO_SIN - z;
  const cx = (CAM.x - CAM.y) * ISO_COS, cy = (CAM.x + CAM.y) * ISO_SIN - CAM.z;
  return [(sx - cx) * CAM.zoom + W / 2, (sy - cy) * CAM.zoom + H / 2];
}

World ‌in, ⁠screen ⁠out, ​every ‌frame, ​with ​no ​canvas ​transform ‌anywhere ​in ‌the file. ​The ‌dot ​screen ‌stays ​pinned ​to ‌the ‌frame ‌— ​which ​is ​exactly ​right, because ‌every ​frame ​of ​this ‌film ‌is ‌its ‌own ​print.

The ​rig ‌is ​fourteen ‌keys ‌that ‌read ‌as ​nine ‌shots. ⁠Position ⁠eases ​on ‌a ​cubic; zoom ​interpolates ​in ​log ‌space, ​because ‌the ​film ‌goes ​from ‌0.5 ​to ​2.7 ‌and back, ‌and ‌a ​linear ​zoom ​across ​a ‌change ​that ​size ​races ‌at ‌the ‌wide ‌end ​and crawls ​at ‌the ​tight ‌end. ‌No ‌crane ‌does ​that.

js
CAM.zoom = Math.exp(lerp(Math.log(a.zoom), Math.log(b.zoom), u));

Knockout is occlusion, and it will erase your family

Here ‌is ⁠the ⁠law ​that ‌cost ​the ​most. ​In ​this ‌engine ​a ‌bright ​ink ‌does ​not ‌print over ​a ​dark ‌one; ‌it ‌punches ​a ​hole ​in ​every ‌darker ​layer ​first, ​and ‌fills the ‌hole. ‌That ‌is ​what ​a ‌riso ​separation ‌is, ‌and ‌it ‌is ​why ‌the ⁠film ⁠looks printed ​rather ‌than ​muddy.

The ​consequence ​nobody ​warns ‌you ​about: ‌a ​knockout ‌is ​an ‌occlusion ​test ​with no ‌depth ‌buffer. ‌Whatever ​knocks ​last ​wins, ​whatever ‌it ​covers ​is ​gone. ‌Four separate ‌times, ‌the ‌same ​symptom ​— ‌a ​person ‌printed ‌as ‌a ‌white ​paper ‌slab ⁠— with ⁠four ​different ‌causes.

  1. The ​figure ​knocked ​its ​own ‌ink. ​A ‌person ​is ‌five ​boxes; ‌a ​box ​is ‌three faces. ‌Each ‌face ​erased ​the ​one ​before ‌it, ​so ​the ​whole ‌figure ‌came ‌out ‌as bare ​paper. ​One ‌line ​in ‌paint(), ‌and ‌it ‌fixed ​both ‌films:

    js
    for (const k of knock) {
      if (k === ink) continue;   // NEVER knock your own ink
    
  2. The ⁠lamp's ⁠cone ​was ‌drawn ​after ​the ​people. ​Split ‌into ​lampGlow() (with ‌the ​shell) ‌and ​bulb() ‌(the ​fitting, ​late).

  3. The ‌next ‌room's ‌floor ​slab ​was ​drawn ​after ‌them. ​A ​floor ​is ‌a ‌horizontal plane, ‌so ‌in ​isometric ​it ‌projects ​up ‌and ‌over ‌the ‌room ​behind ‌it. ⁠The shop's ⁠customer ​was ‌being ​erased ​by ​the ​floor ‌of ​the ‌store ​next ‌door.

  4. The ​television's ‌beam ​knocked ​indigo, ‌so ‌the ‌light ​deleted ​the ​sofa ​and the ‌family ​it ​was ​supposed ‌to ‌be ‌falling ‌on.

A dark isometric sitting room where a cyan wedge of television light has erased the sofa and the peopleThe same room where the television light falls across a sofa and two seated figures
Left: the sitting room after the TV beam was given its own pass — and the beam knocked the night ink AND the furniture, so the family it lights disappeared. Right: the same frame with the beam knocking only violet.

There ​was ​a ‌fifth ​version ‌of ‌the ‌same ‌mistake, ​upstream ‌of ⁠all ⁠of ​them. ‌The sitting ​room ​was ​first ​lit ‌the ​way ‌every ​other ‌room ​is ‌lit ​— ​a ‌lamp, ‌tinted blue ‌— ​and ​it ​came ​out ‌beige. ​A ​room ​lit ‌by ‌a ‌television ‌is ​a ​dark ‌room with ​one ‌bright ‌thing ‌in ‌it, ​and ‌you ⁠cannot ⁠get ​there ‌by ​dimming ​a ​lamp, because ​the ‌night ​pass ‌is ​printed ‌over ​the ‌room's ​own ​television.

A washed-out beige isometric sitting room with no clear light sourceA dark isometric sitting room lit only by a bright cyan television
Left: the sitting room lit like a room with a lamp in it, with the lamp tinted cold. Right: the room taken down to a sixth of that and its television moved into a pass that runs AFTER the night ink.

Causes ‌1–3 ‌produced ‌the ​same ​three ​render ​passes ‌the ​file ​has ​now:

  • SHELL ‌— ‌every ‌floor, ‌wall ​and ​lamp ‌cone ​in ‌the ‌house, ‌laid ‌first.
  • STUFF ​— ‌everything ⁠that ⁠stands ​on ‌a ​floor, ​back ​to ​front.
  • GLOW ‌— ​anything ‌that ​is ‌a ​light. ‌This ​pass ​exists ‌because ‌the ‌night ​ink is ​printed ​over ​a ‌whole ​room ​after ​its ‌contents: ‌without ‌it, ‌the ​darker ​you make ‌a ​room ‌the ‌darker ‌its ‌own ​television ‌gets, ⁠and ⁠the ​set ‌can ​never ​be ​the brightest ​thing ‌in ​its ‌own ​shot.

When something is invisible, render it in another ink

The ‌stirring ​cook ‌in ​the ​kitchen ‌was ‌drawn, ‌positioned ​correctly, ​and completely ​invisible ​— ‌indigo ​at ​0.68 ​over ‌a ‌lamp-lit ‌floor ‌is ​a ​smudge. ‌I spent ​longer ‌than ‌I ‌should ‌have ​deciding ‌whether ⁠the ⁠figure ​was ‌missing ​or merely ​unreadable. ​The ​answer ‌took ​one ‌line ​and ‌one ​screenshot:

An isometric kitchen where the cook is rendered in bright pink, standing between the counter and the stove
The same frame with one argument changed: ink 'pink'. The figure was there all along, exactly where the arithmetic said. A missing object and an unreadable one need completely different fixes, and this is the cheapest way to tell them apart.

The ‌fix ​was ​the ‌silhouette ‌rule ‌restated ​at ​prop ​scale: ​a ‌figure ​reads ​at ​0.9 of ‌an ‌ink, ‌not ‌0.7 ​— ​and ‌it ​reads ‌because ‌of ‌its ‌shadow ​as ‌much ⁠as ⁠its ​tone. Every ‌person ​now ​drops ​a ​violet ‌pool ​at ‌their ​feet ‌before ​any ‌of ​their ​boxes ‌go down. ‌Without ‌one, ​a ​figure ​in ​isometric ‌floats ​a ​few ​inches ‌above ‌the ‌ground and ‌the ​eye ​knows ‌it ​even ‌when ‌it ‌cannot ‌say ​why.

Light is not a hole

A ‌pool ⁠of ⁠lamplight ​that ‌knocks ​the ​night ​out ​and ‌then ​prints ‌one ​pale ‌ink ​is bare ‌paper. ​Bare ​paper ‌does ‌not ‌read ​as ​light; ​it ​reads ‌as ​a ​hole ​in ‌the picture ‌— ‌which ‌is ​precisely ​what ‌the ​first ‌kitchen ‌looked ‌like, ‌and ​what ‌the television ⁠beam ⁠looked ​like ‌from ​the ​next ​room ​over.

Light ‌is ​a ‌bright ​ink ‌over ​a ‌trace ​of ​the ‌thing ‌it ‌is ​lighting. ​Every ​pool ​in the ‌film ​now ​puts ​a ‌little ‌violet ‌back ‌inside ​itself, ​which ‌is ​also ‌how ‌a two-colour ‌riso ‌prints ​a ‌highlight.

The ⁠same ⁠idea ​one ‌scale ​up: ​a ​lit ​wall ‌is ​not ‌a ​flat ‌slab ​of ‌colour. ​One bulb ​throws ‌more ‌light ‌at ​the ​wall ​beside ​it ‌than ​at ​the ​far ‌corner, ‌and ‌the eye ‌reads ​that ​gradient ‌as ​the ‌size ‌of ‌the ‌room. ​Six ‌bands ⁠along ⁠the ​wall's ‌own depth ​is ​enough ​— ​below ‌four ​it ‌stripes, ​above ‌eight ​you ‌are ​paying ​for ‌a gradient ‌nobody ‌can ​see ​through ​a ​halftone ‌screen.

An isometric kitchen lit by a flat slab of yellow on the left wall with very little in the roomThe same kitchen with a louvred window, hanging saucepans, jerrycans, a basin and a cook at a charcoal stove
Left: the kitchen when the wall was one flat yellow quad and the room's contents were three props in the middle of the floor. Right: the same shot with the wall banded, a louvred window and a rack of sufurias hung on it, and the cook at full ink with a shadow. A lit wall with nothing on it is a colour field — the eye needs something to read the light ACROSS.

The problem a cutaway house has that a room does not

At ​the ​zoom ​that ‌makes ‌one ‌room ‌legible, ​three ​other ‌rooms ​are ‌still ‌in ‌frame, at ‌full ​ink, ‌competing ⁠for ⁠the ​eye. ‌And ​you ​cannot ​fix ​it ‌by ​turning ‌their lamps ​down: ‌the ​light ‌in ​the ​shop ‌is ‌real, ‌and ​dimming ​it ​to ​help ‌the composition ​is ​a ​lie ‌the ‌next ‌shot ‌exposes.

The ​first ​attempt ‌was ​a ‌gobo: ‌one ‌more ‌pass ​of ‌violet ⁠over ⁠every ​room except ‌the ​subject, ​with ​the ​subject's ‌silhouette ​punched ‌out ​of ‌it ​by ‌winding — ​the ​outer ‌rectangle ‌wound ‌one ​way, ​the ​room's ​hexagon ‌the ​other, ​one ​nonzero fill. ‌It ‌changed ‌almost ‌nothing. ​Then, ​when ‌I ​strengthened ‌it, ‌it ‌printed ‌the entire ​house ‌black.

An isometric house frame almost entirely drowned in dark blue inkThe same frame where the winding hole has filled its own outline, blacking out the whole house
Two failures worth keeping. Left: the veil at full strength, drowning the room it was supposed to isolate — because one ink can only get so dark, a 60% violet is a 60% DOT SCREEN and the red and green underneath read straight through the gaps. Right: the winding trap. A hole larger than the shape it is cut from does not empty that shape; it fills its own outline instead.

Three ⁠things ⁠came ​out ‌of ​that, ​and ​together ​they ‌are ​the ‌shot.

A ​dark ‌room ​is ‌two ​inks. ​One ‌ink ‌at ‌60% ​is ​a ​dot ​screen ‌at ​60%: ​everything under ​it ‌reads ‌through ‌the ‌gaps. ​A ​rich ‌black ​on ‌a ‌riso ‌is ‌an ​overprint, ‌so ⁠the night ⁠pass ​lays ‌violet ​for ​the ​tone ​and ‌indigo ​under ‌it ​to ‌close ​the ‌screen.

Use ​a ​clip, ‌not ‌a ‌winding ​rule. ​A ​clip ​is ‌how ​you ​say ​"this ‌ink, ‌but ‌not there". ‌paint() ​gained ​a ‌clipFn, ​applied ‌with ‌evenodd ‌around ‌the ​fill.

Two ‌holes, ⁠not ⁠one. ​A ‌single ​hole ​leaves ​a ​hard ‌hexagon ​on ‌screen, ​because the ‌step ​at ‌its ​edge ​is ‌the ‌whole ‌depth ​of ​the ​night ​ink ‌and ​the ​eye ​finds ‌a straight ‌line ‌of ‌that ​length ​instantly. ‌Half ​the ‌veil ‌stops ‌at ‌a ​near ‌hull ⁠and half ⁠at ​a ‌far ​one; ​the ​edge ​arrives ‌in ​two ‌steps ​and ‌stops ​being ‌an ​edge.

And ​then ‌the ‌thing ‌none ​of ​it ​could ​do. ‌In ​isometric, ​the ​shop's ‌shelves genuinely ‌stand ‌in ‌front ​of ​the ‌kitchen ​a ‌floor ‌above ‌— ‌they ​overlap ‌on screen, ⁠and ⁠no ​amount ‌of ​ink ​over ​a ​volume ‌changes ​which ‌pixels ​they ‌own. ​A 2D ‌hole ​cannot ​separate ‌two ‌rooms ‌that ​occlude ​each ​other.

So ​the ‌last ​move ​is ​not ‌a ‌lighting ‌trick ‌at ​all, ​it ‌is ​detail ‌falloff: ‌the rooms ‌that ‌are ​not ‌the ⁠subject ⁠of ​a ‌shot ​keep ​their ​shell ​and ‌lose ​their contents. ‌The ​house ‌stays ​whole, ‌the ​competing ​colour ‌goes ‌away, ‌and ​the ​frame gets ​cheaper ​at ‌the ​same ​time.

The frame everybody screenshots is the one in the middle of the crane

With ​detail ‌falloff ‌in, ‌a ‌new ​defect ​appeared ‌exactly ​where ‌nobody ‌looks: ‌the 1.5 ‌seconds ​the ‌camera ⁠takes ⁠to ​crane ‌from ​the ​shop ​to ​the ‌kitchen. ​The ‌shop still ​owned ‌the ​focus, ‌so ​the ​kitchen ‌arrived ‌lit ‌and ​completely ​empty.

A mid-crane frame in which the upper room is lit but contains nothingThe same frame with the kitchen above and the shop's shelves below both fully dressed
Left: t = 13.0s, mid-crane, with the kitchen lit and bare because the shot below it still owned the detail. Right: the same instant with both rooms dressed. Two rooms are dressed whenever a shot is arriving or still leaving, and the hand-over happens at the midpoint of the move rather than at whichever key happens to be first in the list.

The ​shot ​it ‌buys ​is ​my ​favourite ‌frame ‌in ‌the ‌film: ​the ​dolly ‌from ​the ‌kitchen to ‌the ‌sitting ‌room, ​where ‌the ⁠two ⁠rooms ​sit ‌side ​by ​side ​at ​two ‌colour temperatures ​— ‌filament ​on ‌the ​left, ‌television ​on ​the ‌right.

An isometric frame with a warm lamplit kitchen on the left and a dark sitting room lit blue by a television on the right
t = 20.0s. Warm and cold in one frame, which is the only argument the film makes about what a house at night actually looks like.

Two more that only a screenshot finds

A ‌lamp ‌on ​a ​roof ​is ​not ‌a ​bulb ​in ​a ‌room. ‌lampGlow() ‌drops ‌a ​wedge ​120 units ‌straight ​down ‌the ‌screen, ‌which ‌is ​right ‌for ⁠a ⁠bulb ​hanging ‌over ​a floor ​and ​absurd ​for ‌a ​tadooba ‌on ​a ‌parapet: ​with ‌no ​floor ​under ‌it, ‌the ‌wedge hangs ​off ​the ​side ​of ‌the ​building ​like ​a ‌tractor ‌beam.

An isometric house at night with a yellow cone of light hanging off the roof into empty airThe same house with a lamp and a seated figure on the open half of the roof
Left: the roof lamp with a room's light cone on it. Right: the same shot with the cone removed and the light given a terrace to fall on — which also meant laying the terrace, because drawSlabs skips the top face of every slab and there was nothing up there to stand on.

And ‌an ‌empty ​room ​is ‌not ​a ‌dark ‌room, ‌it ‌is ​an ‌empty ⁠room. ⁠The ​bedroom ‌was ​a bed ​and ​a ​cupboard ‌in ​a ‌cream ​field ‌until ​it ‌got ​what ​the ‌room ‌actually ‌holds: a ​mattress ​on ​the ​floor ‌for ​the ​younger ​ones, ‌a ‌suitcase, ‌a ‌louvred ​window, and ​school ‌uniforms ​on ‌a ‌line.

A pale isometric bedroom containing only a bed with a net and a cupboardThe same bedroom with a floor mattress, a suitcase, a window and school uniforms on a line
Left: the bedroom with a bed, a net and a cupboard. Right: the same room dressed, with the mosquito net changed from a wireframe tent to the one genuinely translucent thing in the film — it does not knock, because you are meant to see the sleeper through it, and an overprint is exactly what gauze does to a photograph.
An isometric frame craning up to a lamplit bedroom with the television-lit sitting room below
t = 27.5s. Arriving at the bedroom, with the sitting room's television still throwing light across the bottom of the frame. The film never cuts; it is one continuous move for forty-two seconds, which is what makes the house feel like one house.

The harness earns its keep twice

scripts/riso-shots.mjs ‌is ‌the ​same ‌screenshot ⁠harness ⁠as ​the ‌first ​film, extended ​rather ​than ​copied ‌— ​one ‌harness ​per ‌animation ​is ‌how ​the ​checks ‌drift apart. ‌Two ‌additions:

--cost ​sweeps ​the ​timeline ​and ‌reads ​the ​page's ​own ‌render(t) ‌timing, because ‌a ‌film ​that ​is ‌a ​pure ‌function ‌of ‌time ‌can ​still ‌miss ⁠60fps, ⁠and ​the wide ‌shots ​draw ​a ​hillside ​of ‌neighbouring ​houses ‌while ​the ‌room ​shots ‌draw none ​of ​them.

frame cost: mean 4.5 ms, worst 11.6 ms at t=41.5  (60fps budget 16.7 ms)

Hook ‌parity. ‌The ‌rooms ​film ​originally ​exposed ​__risoShot ‌/ ​__risoShots where ​the ​first ‌film ‌exposed ‌__risoScene ‌/ ​__risoScenes. ​The ‌harness's --all-scenes ​flag ‌therefore ‌did ‌nothing ‌at ​all ‌on ⁠the ⁠new ​film, ‌silently. ​A hook ​that ​is ​only ‌almost ​the ‌same ​is ‌a ​harness ‌that ​quietly ​skips ‌your artifact. ‌Both ‌films ​now ​answer ​to ​the ‌same ​two ​names.

A wide isometric night view of a Kampala hill with many houses, most with one lit window, and a moon
t = 41.5s. The pull-back: our house is one lit window among thirty, which is the whole point of the last shot.

What it cost

Counted from the Claude Code session log over the rooms film only; this post is not in it. Cache reads dominate by two orders of magnitude for the same reason as last time — the loop is re-read the file, change forty lines, screenshot, look, a few hundred times over.
measurevaluehow it was counted
wall clock3 h 02 minfirst line in the session log mentioning riso-rooms (15:43Z) to the last (18:45Z)
output tokens391,767sum of output_tokens over 350 assistant turns in that window
cache reads83.45 Mcache_read_input_tokens over the same window
cache writes668,981cache_creation_input_tokens over the same window
the artifact91 KB / 1,844 linesone index.html, no dependencies of any kind
frame cost4.5 ms mean / 11.6 ms worstthe page's own render(t) timing, swept at 0.5s intervals across 42s

Under ​half ‌the ‌time ‌of ‌the ​first ​film, ‌on ​a ‌harder ‌renderer, ‌because ‌the ​engine already ‌existed ⁠and ⁠the ​method ‌was ​already ​known. ​That ​is ‌what ​a ‌ladder ​is for.

Credits, plainly

  • The ‌premise ​— ‌an ​isometric ​riso ‌world ‌of ‌cutaway ​rooms, ​and ​a ​film ‌that walks ​one ​light ​through ‌them ‌— ‌is ‌Kevin ​Ngo's (@kevin_t_ngo), ​from ‌"a ​small ‌light, ‌room ‌by room", ‌also ​made ‌with ⁠Claude ⁠Code. ​None ‌of ​his ​code ​was ​published ‌and ​none of ‌it ​is ‌here; ​what ‌he ​provided ​is ‌the ‌proof ‌that ​the ​idea ​works.
  • The ​workflow ‌— ​plan ​the ​rooms, ‌block ‌out, ‌screenshot, ‌dress, ​then ​animate — ‌is ​the ‌one ‌published ‌as ‌the riso-rooms Claude Code skill, which ​credits ‌the ⁠same ⁠original.
  • The ​engine ‌is ​ours, ​from ​Nsenene Season, ​which ‌was itself ​built ‌against ​an ‌r/ClaudeAI ​brief ‌credited ​in ​full ‌in ‌that ‌post ​and ​in that ​file's ​header.
  • Mine, ‌for ​the ​avoidance ​of ‌doubt: ‌the ‌isometric ‌layer ​(projection, ​camera rig, ‌the ​three ‌passes, ‌occlusion ‌by ‌knockout), ​the ‌two-hull ⁠gobo ⁠and ​the detail ‌falloff, ​the ​light ​model ​in ‌which ​a ‌lit ​room ‌is ​the ‌night ​ink ​removed, the ‌Ntinda ‌plot ‌and ​everything ​standing ​in ​it, ‌and ​every ​bug ​listed ‌above.

The ‌page ‌carries ‌the ​same ​credit ‌under ​its ‌own ‌controls ‌and ‌in ​a ‌header ⁠comment in ⁠the ​source, ‌so ​it ​travels ​with ​the ‌artifact ​rather ‌than ​only ‌with ​this ‌post.

boracode.ai · Ronald Adonyo · ettaala-room-by-room
content hash 42a9c23cbd34
A wide horizon at dusk: layered hills in muted plum and ochre above a lake that holds the last light, a family of crested cranes at the water's edge, an acacia in silhouette and a single bright star above the ridge.
Ettaala: walking one lamp through an isometric house, room by room — boracode