Low-poly Isometric Island 低多边形等距

The Island That Grew·53s·Lemo-Opuscarby Lemomo

A tiny faceted world seen through an orthographic isometric lens: everything is a geometric block, and everything that appears arrives with a bounce and a note.

The demo: An island and its village grow tile by tile from an empty sea, each tile a note, into a starry night.

Game trailersWorld buildingCalm content
Style prompt · STYLE.md

A tiny faceted world seen through an orthographic isometric lens: everything is a geometric block, and everything that appears arrives with a bounce and a note. References (grammar only): Townscaper (building is the performance; every piece lands with a springy bounce and a sound; pieces sprout small details by themselves); Monument Valley (isometric geometry, soft pastel palettes, gradient skies, calm pacing, lots of empty space). Never copy their grids, buildings, characters, UI or typography, and never name them on screen.

1. Essence, and what it is not

A miniature world built from flat-shaded low-poly geometry (hexagonal tile columns, box buildings with prism roofs, cone and icosahedron trees) floating in a faceted sea or void, watched by an orthographic camera at an isometric angle. No perspective and no horizon: distance dissolves into a screen-space sky gradient, so the world looks like a model suspended in colour. Light, not texture, tells time: every facet changes colour as the sun moves; in the dark only emissive blocks keep their colour.

Not a voxel game (no cubes-of-cubes, no pixel texture), not a tilt-shift photograph of a real place (no perspective, no photo detail), not a smooth-shaded 3D cartoon (no rounded normals, no subsurface).

2. Materials & rendering

  • Geometry (world unit = one hex tile's circumradius): pointy-top hexagonal columns (CylinderGeometry(r, r, h, 6)), axial → world x = √3(q + r/2), z = 1.5 r. Terrain height encodes material (sand low, grass, hills, rock highest). A thin separate top cap sits over a darker side colour; sink the side column's top below the cap or it z-fights. Leave a small gap between tiles so AO draws the seams.
  • Props are primitive assemblies: boxes, prisms, low-segment cylinders and cones, icosahedra at detail 0, pinched boxes. Emissive parts (windows, lamps) get their own material per object so they can light individually.
  • Materials: MeshStandardMaterial({ flatShading: true, roughness ≈ .9 }), no textures. Jitter each tile's lightness a few percent so the ground isn't a spreadsheet.
  • Water (if any): a dense non-indexed plane displaced by summed sines in the shader, flat-shaded so each triangle glints on its own (not too low a roughness or facets flash white); a coarse plane below for zoom-outs; a shallow map redrawn per frame where land exists, mixing deep → shallow colour with a faint foam rim; a thin transparent ring at each tile's water line for surf.
  • Sky without a horizon: in post, linearise the orthographic depth and blend distant pixels into a vertical screen gradient; set haze start/end as fractions of frustum height × cot(elevation) so the horizon band stays in the top third at any zoom. Stars (hash grid, twinkle) only where haze is thick.
  • Post: MSAA + depth → GTAO (mandatory: it draws the seams between tiles and under roofs) → sky/haze composite → a very light tilt-shift at the frame edges → bloom (stronger at night) → pastel grade → Neutral tone mapping. Supersample 2×.
  • Light beams (searchlight, projector, torch): a camera-facing ribbon with a Gaussian profile, bright at the root and fading with distance, plus a wider faint halo copy; light the surface under it in its own shader.

3. Colour logic

  • Pastel, low contrast, a handful of hues: one ground family (sand/grass/earth or its equivalent), one neutral rock, and a small set of wall and roof colours repeated across buildings.
  • Time of day is a keyframed set of colours (zenith, horizon, deep, shallow, sun colour, intensity, elevation, azimuth), interpolated. Day is airy and cool; dusk warms the horizon; night desaturates everything toward blue-violet.
  • At night, warm emissives are the only bright things (windows, lamps, a beam). Keep the sun sideways to the camera in key shots (low sun behind the target turns faceted water into glare); water receives no shadows.
  • No outlines, no black; shadows and AO are the only darks.

Examples of world palettes: sand, grass and coral roofs under a pale-blue day; snow tiles, pine greens and red cabins under a lilac sky; terracotta mesas, sage scrub and turquoise pools under an apricot dusk.

4. Type & subtitles

  • Geometric OFL sans faces: a rounded one for subtitles (e.g. Quicksand 600), a geometric caps face with wide tracking for titles (e.g. Josefin Sans).
  • Subtitles: no box; warm white with a soft dark-blue drop shadow, centred low; a small flat-shaded tile icon beside them whose top colour follows the time of day. Hold ≥ max(1.8 s, speech + 0.6 s), never overlapping the next line or a title.
  • Titles behave like tiles: letters rise one by one from under a water line, overshoot and settle.
  • A text card over the world leaves a gap where the world stays visible.

5. Motion quality

  • On ones (24 fps), smooth. The charm is in the arrival curves, not stepping.
  • Rise from below: a short cubic ease up, then a damped-sine overshoot; a splash ring and spray the moment it breaks the surface. The event time is when it settles.
  • Drop and stack (floors, roofs, rings): a fast ease-in fall, a damped bounce, and volume-preserving squash-and-stretch on landing.
  • Pop (trees, lamps, details): scale 1 − e^(−k s)·cos(ω s), about 25 % overshoot.
  • Life: mills turn, smoke puffs, birds circle, boats bob with the same wave function as the water (evaluated on the CPU).
  • Schedule growth on the music grid: at most one piece per smallest slot, so every event keeps its own note; accelerate by density (halves → quarters → eighths), not by tempo.
  • Never moves: the projection (no perspective, no fov change).

6. Camera grammar

THREE.OrthographicCamera. Frustum height is the zoom (a few units for a close-up, the whole world at a dozen or more, hundreds for a scale reveal); place the camera ≥ 3 × frustum height away or the bottom of the frame looks under the sea. A vocabulary, not a route. Opening and ending come from the topic.

Move What it expresses Can serve
Turntable (azimuth sweep) the thing seen from every side building; inspecting; a product assembled
Pure orthographic zoom (frustum change) scale without perspective a detail in context; smallness; a world among worlds
Elevation change low (≈20–25°) widens the sky band; high (≈50°) reads as a map night and stars; a sweep across the whole world; a plan
Locked 3/4 wide the world lives on its own lights coming on; time-lapse; waiting
Tight close-up on one arrival the bounce and the splash a key piece; a milestone; a gag
Side angle trailing a mover travel across the frame, never at the lens a delivery; a departure; a route
Lateral truck along a row of tiles a list, a sequence steps of a process; a timeline
Time-lapse sun sweep, camera still light is the time machine a day; a season; history

Intercut wide and close on beats: wide shows the shape growing, close sells the bounce. Transitions: hard cuts on the beat, a time-of-day change, a zoom through the sky; no dissolves, no perspective whip-pans.

7. Sound palette

  • Music can be event-driven: growth events carry a note, and the score synthesizes each one at its exact time, so the picture writes the tune. Pitched percussion suits the facets: marimba, kalimba, woodblock, bells, vibraphone, celesta, steel drum; underneath, a soft pad, a gentle ostinato, light shaker or soft kick. Pentatonic or modal material keeps any order of notes consonant. No piano, no strings.
  • A single note can be a motif: one sound owned by the world (a bell, a chime, a horn) that returns changed.
  • Foley follows material, all synthesizable: bubble sweep + splash with droplet ticks for rising tiles (bigger = lower); wooden knock plus a quieter bounce knock for floors; ceramic clack for roofs; hollow plank knock; heavy stone thunk; glass tink; a low whump with electric hum for anything switching on.
  • Beds: low-passed brown-noise water with swells, birds as downward-gliding FM chirps, creaking wood, crickets or wind at night.
  • Silence: drop to a low drone and a bed; a moment of digital zero on the music bus before a big downbeat. Options: thin the ostinato to one voice instead of stopping it; let the world's own events carry the rhythm for a passage.
  • Voice: clear and gentle, few short lines; avoid words that collide with the film's subject (a film about notes: "sea" heard as "C").
  • Mix: voice compressed and well above the music, music ducked under it, −14 LUFS; grain 0 (flat colour shimmers with grain).

8. Native moves

A menu: use the ones your story needs.

  • Building is composing: every piece that lands plays a note; a later sweep can replay them in spatial order as a finished melody. Fits content like: a skyline whose floors are notes; a team assembling a product; a garden whose flowers form a chord.
  • Procedural growth: tiles rise, floors stack, trees pop. Fits content like: a startup's first year; a coral reef recovering; a neighbourhood being planned.
  • Scale by pure zoom: one object fills the frame, then the world becomes a speck (or the reverse). Fits content like: one user among millions; a village on a continent; a data centre inside a network.
  • Turntable: a continuous rotation around the thing being made. Fits content like: a machine being assembled; a museum object; a house renovation.
  • Light as clock: shadows sweep and facets re-colour. Fits content like: a day of a shop; a year of a farm; a shift at a hospital.
  • Emissive blocks in the dark: lights coming on one by one, a beam sweeping like a clock hand. Fits content like: a network going live; a town at nightfall; volunteers joining a cause.

9. Pitfalls of the medium

  • perspectiveDepthToViewZ in shared post code is wrong for an orthographic camera → orthographicDepthToViewZ.
  • GTAO renders sprites and transparent objects as solid quads → hide them during the AO pass; set visible = false at zero opacity.
  • Side column and top cap sharing one plane → z-fighting → sink the column.
  • A double-sided additive cone reads as two white lines → a camera-facing Gaussian ribbon + halo.
  • Camera too close for its frustum → the frame bottom shows below the sea → distance ≥ 3 × frustum height.
  • Two pieces landing in one grid slot muddy the melody → one piece per slot.
  • half is a reserved word in GLSL.

10. Engine

In demo/ (three.js): world.js (hex layout, primitive props, growth schedule → events with notes, arrival curves), sea.js (faceted wave shader, shallow map, beam-lit water), post.js (ortho post: GTAO → sky/haze + stars → tilt-shift → bloom → grade), story.js (beat grid, time-of-day keyframes, shots), main.js (renderer, lights, per-shot cameras, HUD, event export), music/score.py (event-driven score; music/check.py compares audio onsets and pitches with the events), mix.py (foley, beds, ducking). File map and build steps: DEMO.md.

11. Variation space

You decide the world (island, town, orchard, factory floor, planet), what grows or changes, the palette within the colour logic, the opening, the ending, the camera path and the pacing. All far from our demo:

  • Structures: erosion (a finished world loses pieces one by one, each removal a falling note, until only the essential piece remains); two worlds side by side trading pieces across a gap until they join; a year in four seasons on one fixed world, the palette keyframes carrying the story.
  • Openings: mid-construction (a busy half-built world, many pieces already dropping); from above (a top-down map that tilts down into isometric); the full world first, then a push into one tile where the story lives.
  • Endings: a close-up on one final piece landing, no pull-back; the world packed away (pieces fly up and stack into a box); daylight (dawn floods the world and the music resolves on the first sunlit facet).

How our demo was made (story, shots, score, end card, build): DEMO.md. Read it after your treatment exists.

Director's notes for the demo film

One example among many. Don't reuse its story, arc, shots, props or timings.

Demo: The Island That Grew (53 s) · lowpoly-island.mp4 · source in demo/

Story & structure

An empty sea with only a buoy that knows one note; tiles, trees and houses rise from the sea one by one, each landing with a note, so the island and its song are written together — until at night the boat that went out cannot find its way home, and the last "note" is a lighthouse.

We read the brief as: a small world that grows, lives through a day, and meets one problem that the final piece solves — one subject (an island), one goal (to become a home), one turn (night falls, the boat is lost). In the old guide this was offered as the way to adapt any topic; it is only this film's shape.

Emotional arc (53 s): emptiness → first note → accelerating joy of building → a lived-in day → dusk warmth → silent night with one thing missing → the final piece ignites (climax) → pull back to scale, with an echo of the opening.

Native powers spent: procedural growth (the whole first act), the orthographic gaze (the final ×25 zoom), turntable (the growth shot), flat shading × light (the day time-lapse), emissive blocks (windows at dusk, the beam at night).

"Building is composing", as the demo paid it off: every growth event is exported from the page with a MIDI note, and the score's melody voice is synthesized from those events. The lighthouse beam turns once every 12 beats and passes one ring cell per beat; the eight houses standing on those cells light up and chime as it passes, so the notes laid down in a shuffled build order are replayed in spatial order as the complete theme. The second ring around the centre cell has 12 cells at exactly 30° steps, which made this beat-synced sweep possible.

Shots

Beat Camera Why
Opening Close ortho (frustum 7.5) on one lonely object, empty faceted sea, sky band at the top Give the viewer nothing to look at; the first tile then becomes an event
Growth Turntable (azimuth +45° over ~5 s) with a slow zoom-out, intercut on beats with close-ups: a peak rising with a splash (elevation 23°, frustum 5.2), floors stacking (26°, 6), dock planks and the boat drop (27°, 6.6) Wide shots show the shape growing; close-ups sell the bounce and the splash
Day Medium on the village, sun sweeping 100° in 2.5 s (time-lapse shadows) Light is the time machine
Departure Side angle computed from the dock direction, camera trails the boat The boat must move across the frame, not toward the lens
Dusk Fixed 3/4 wide, slow push, windows light one per eighth note Let the lights be the only motion
Night Very wide, elevation 21°, island lower left, one tiny light far out in the dark Negative space = danger
Climax High angle (50°) turning at 0.22× the beam speed, then easing down to 25° so the starry sky "rises" You must see the beam sweep over every house; then the sky opens up
Ending Pure orthographic zoom ×25; an echo object far away in the lower right Scale reveal and echo

Camera settings used: azimuth 45° by default, elevation 30° (build), 33–35° (day detail), 20–25° (night — a lower angle widens the sky band so stars show), 50° (top-down-ish when the beam sweeps). Frustum height 5–7 for close-ups, 12–16 for the whole island, up to 420 for the final scale reveal.

Score structure

  • Event-driven: page EV entries carry note; music/score.py synthesizes every one at its exact time (rise → marimba, pop → kalimba, floor → woodblock, roof → kalimba+marimba = the melody, window → soft kalimba, ring → rising marimba, beamhit → kalimba lead + marimba octave + bell shimmer). Underneath: ambient pad, a gentle marimba ostinato and shaker/soft kick only in the day and climax sections. 96 BPM, D major pentatonic feel. No piano, no strings.
  • One note as a motif: a buoy bell on D5 opens the film ("the only note the sea knows") and a distant bell plays the same note at the end.
  • Silence: the night section drops to a low pad drone, waves and crickets; the music bus is digital zero for the last half-beat before the climax downbeat.
  • Growth scheduled on the 16th grid: tiles on halves → quarters → eighths.
  • The boat's answering bell sits in a rest of the melody, filling the gap when the beam finds it.
  • Foley (mix.py): as in STYLE.md §7, plus gulls by day (downward-gliding FM chirps), windmill creak, crickets from dusk; the dock plank knock and lighthouse-ring stone thunk, glass tink for the lamp room, whump + hum on ignition. Floors: second knock 0.21 s after the first.
  • Voice: Kokoro af_sky (clear, gentle female), speed 0.86–0.92, 5 short lines. "Sea" was heard as "C" in a film about notes, so the narration says "ocean".
  • Mix: voice compressed, ~10 dB over the music; music ducked −8 dB under the voice; −14 LUFS; grain 0.

Palette & props

  • Terrain heights: sand 0.36–0.42, grass 0.74–0.9, hills 1.1–1.4, rock peak 1.95; top cap 0.16 thick, side column sunk 0.1 below it; 1.5 % gap between tiles; lightness jitter ±4 %; roughness .88.
  • Props: box houses 0.92 wide, 0.62 per floor, gable roof prism (overhang 0.08) with wall-coloured gable ends; windows = small quads with their own emissive material per house; cone pines (6 sides), icosahedron (detail 0) broadleaf trees, 3-sided-cone palm fronds; windmill (6-sided tapered tower, 4 blades); dock planks on posts; a boat from a pinched box hull + triangle sail; lighthouse = 4 tapered 8-sided rings alternating white/red + dark gallery + emissive glass + red cone cap. Three gulls circle; chimney smoke; the windmill spins up on the "island complete" chord.
  • Palette (sRGB): sand top #f3dfab / side #e2c68e; grass #93c96c, #7fbd62; earth side #c99c6e; rock #b4b8bd / #8f949c; walls #fbf3e6 #f5c2b0 #b5dccb #a9cbe8 #f7df8f #e9c9e6; roofs #d9644a #4f7fb8 #e0874f #6a5a8e #3f8f86; pines #3f8a5a #4f9c63; leaves #7fc36a #9ad06f; warm windows #ffc676; lighthouse #f6f2ea / #d9544a.
  • Day/night keyframes (zenith / horizon / deep sea / shallow / sun): dawn #9fb6cf / #f3d2c4 / #4f8ea3 / #8fc9c4; day #88c8e8 / #eaf5f2 / #38a5bc / #86e2d4 sun 2.7; dusk #6f6fa8 / #ffa888 / #5a6a9e / #b08aa6 sun #ff9a68 elevation 8°; night #12163a / #4a4478 / #23264a / #363a66, moon #a9b0e8 0.9. Night sea darker near the camera and brightening toward the sky band.
  • Sea: 220×220 plane, 200×200 segments, four summed sines (amplitude ≈ 0.2), roughness 0.55; coarse 5000-unit plane 0.55 below; lagoon map a 512² canvas of soft blobs where tiles have risen; surf ring 0.99–1.12 r, opacity ≈ 0.32.
  • Arrival numbers: rise 0.5 s cubic ease, overshoot sin(13 s)·e^(−7 s)·0.18, hex splash ring + 14 droplets 0.12 s before it settles. Drop: 2.4 units in 0.22 s (ease-in), bounce |sin(15 s)|·e^(−9 s)·0.22, squash 1 − 0.16·e^(−12 s)·cos(26 s) on Y, volume preserved. Pop: 1 − e^(−9 s)·cos(17 s).
  • Post: GTAO radius 0.7, scale 1.5; tilt-shift max radius 3 px at the frame edges; bloom threshold 0.95, strength 0.22 day / 0.5 night; grade sat 1.04–1.1, soft contrast, small blue lift at night; render 3840×2160.
  • Lighthouse beam: camera-facing ribbon, halo 2.4× wider at 0.32 strength, tilted 7° down so it meets the sea far away; water lit by an angular band around the beam direction plus per-facet sparkles. Beam: one turn every 12 beats.
  • Subtitles as built: Quicksand 600, 44 px, #fffdf8, navy drop shadow (blur 14), centred 118 px above the bottom; the hex icon (top + two side faces) is dawn pink, day grass, dusk orange, night window-yellow. Shown from 0.05 s before the line.
  • Title: Josefin Sans 600, all caps, 78 px, 16 px tracking; letters rise 55 ms apart from under a thin water line; 300-weight lowercase sub-line ("a low-poly island film").

End card

The night scale-reveal frame, darkened; the same title block (static), hex icon, LOW-POLY ISLAND · LEMO-OPUSCAR, LemoLab × Claude Opus 5.5, credits, laid out so the tiny lit island stays visible in a gap between the text blocks. The "LemoLab × Claude Opus 5.5" sign-off (and the LEMO-OPUSCAR line) belongs to this library's demo only; a user's film carries no LemoLab credit and no copy of this card.

Build notes

styles/lowpoly-island/demo/
  story.js   timeline (96 BPM grid), VO, chords, day/night keyframes, shots
  world.js   hex layout, all props, growth schedule → EV (with notes), per-frame animation
  sea.js     faceted wave shader, shallow-lagoon map, beam-lit water
  post.js    ortho post: GTAO → sky/haze + stars → tilt-shift → bloom → grade
  main.js    renderer, lights, cameras per shot, HUD (title, subtitles, end card), EV export
  music/score.py   event-driven score (+ check.py: librosa onset/pitch check vs events)
  mix.py     foley + ambience + voice + ducking     subs.py  subtitle cues
  build.sh   one-command rebuild
  1. node core/render/still.mjs styles/lowpoly-island/demo --range 0.5:52.5:1 + core/render/sheet.py — overview sheets, iterate.
  2. core/tts/tts.py lines.json voices → core/tts/asr_check.py until all OK.
  3. node core/render/events.mjs styles/lowpoly-island/demo → music/score.py → music/check.py → mix.py.
  4. node core/render/video.mjs styles/lowpoly-island/demo --fps 24 --workers 3 (1272 frames, 2× SSAA + GTAO).
  5. sh core/render/mux.sh out/video24.mp4 mix.wav lowpoly-island.mp4 24 0. Or just sh demo/build.sh.

Pitfalls tied to this demo's props:

  • An invisible glow sprite became a black parallelogram floating on the sea in the GTAO pass.
  • The boat was 21 units out — off-screen when the beam found it. We derived the boat's position from the beam's angular speed so the beam finds it on a chosen beat (a rest in the melody, so the boat's answering bell fills the gap).
  • Shadows on the sea at low sun made a long dark stain; the sea receives no shadows.