fuzzyworld

tower / files / world-generation.md

world-generation.md

Blame: the commit that last changed each line · file · history

376f2ce6 2026-09-16 Pavel Safronov1# Tower — Live World Generation
2
00ceeedb 2026-09-27 Pavel Safronov3**Status: IN PROGRESS.** Accepted by the author (D-062). **Phases 1–6 are built**
4(D-063 to D-067, D-069, D-071), with a scenic tour between 4 and 5 (§19, D-068) and the
5Capital given an extent (D-070); phases 7–8 are not. This document replaces the terrain pipeline of D-019 and reverses
376f2ce6 2026-09-16 Pavel Safronov6D-006; see §11 for every existing decision it touches, and §15 for where Phase 1
7departed from the plan and why.
8
9---
10
11## 0. Summary
12
13Today the world is two images. `npm run terrain` reads `content/geography.json`,
14bakes a 2048 × 1192 height raster and a colour raster (264 m per pixel), and the
15interior view displaces one cylinder mesh by the first and paints it with the
16second. Every lake, forest and desert is a colour in a PNG. Nothing is smaller than
17a pixel, and nothing moves.
18
19This proposal replaces that with **features that generate themselves, live, in the
20browser**:
21
22| Feature | What gets generated |
23|---|---|
24| **Mountains** | named peaks at their authored heights, ridges and saddles between them, valleys cut by simulated fluvial erosion, snow and bare rock by altitude and slope |
25| **Lakes** | a basin, a real water surface at a constant level, wind waves sized by fetch |
26| **Rivers** | a valley and channel descending monotonically from source to outlet, a water surface that follows the descent, and flow animated downstream |
27| **Cap seas** | the two ring oceans, with ocean waves and sailing ships on deterministic routes, both driven by Tower's published weather plan |
28| **Desert** | dune fields, rocky hills, terraced plateaus and mesas, and the marks of water that was never there: dry washes, salt pans, abandoned shorelines |
29| **Forest** | individual trees at real sizes, placed by a density field with planned stands and open meadows, and paths through it; a **tall forest** of 110 m giants that stands out as a landmark |
30| **Settlements** | the capital, the stations, and generated hamlets, villages and towns filling out the world: a road skeleton, blocks, lots and blocky extruded buildings |
31| **Weather plan** | Tower's published schedule of wind and storms per body of water, which it can revise at any moment |
32| **Roads & paths** | authored or auto-routed along the terrain, grading the ground they cross, around the capital and through the forest |
33
34There is **no build step**. The world file is the only input. The browser compiles it
35in web workers when the page loads and again whenever the author edits a feature,
36behind a status dialog that says what it is making (§10.1), and streams detail in
37around the observer as they fly through the interior view.
38
39### 0.1 The premise every generator follows
40
41**Nothing inside Tower is a remnant.** Tower is in full control of the inside, and every
42landform, forest, shoreline and wind is there because Tower chose to put it there —
43often because it saw the form on Earth, or on other planets, and judged it appropriate.
44So a dry wash in the Dry Reach is not evidence that water once ran there; it is a
45quotation. The generators still produce physically *plausible* forms — a wash still
46branches the way drainage branches — because Tower copies well. But nothing has to be
47consistent with a history, because there is none. (Author, 2026-09-16.)
48
49---
50
51## 1. Goals, non-goals, constraints
52
53### 1.1 Goals
54
551. **Each concept is a first-class, authorable thing** — placed on the map, given
56 parameters, and regenerated when either changes.
572. **Scale runs from the world to your feet.** 540 × 314 km is readable from 25 km
58 up; a tree, a house and a path are readable standing at 1.7 m.
593. **Live.** Changing a feature shows up in the map and the interior within seconds,
60 without a terminal.
614. **Deterministic.** The same world file produces the same world on every machine,
62 every time. A reader's link to "stand here" shows the author's forest, not a
63 reroll of it.
645. **Canon holds.** Everything in `design.md` §1 still applies: rivers descend
65 because the floor was shaped (§1.9.3), level water is a surface of constant radius
66 (§1.9.3), falling things lean antispinward (§1.9.2), haze is integrated (§1.8).
67
68### 1.2 Non-goals (for this proposal)
69
70- Simulating the world over time (erosion as a process you watch, growth, traffic).
71 Erosion is a *generation* technique here, run once per edit.
72- Clouds and rain are still not drawn (D-049 stands). **Wind and waves are** in
73 scope, as Tower's published weather plan (§4.9).
74- Interiors of buildings, people, vehicles other than ships.
75- Photorealism. The target is "reads correctly and is honest about scale".
76
77### 1.3 Constraints carried over
78
79- **Static hosting.** GitLab Pages, no backend (§3.1). All generation is client-side.
80- **Phones.** The app works below 720 px (D-043). Generation and rendering need a
81 reduced tier, not a separate app (§8.4).
82- **Vanilla TypeScript, Three.js** (D-005). No framework.
83- **Scene units are kilometres** in both 3D views, so shaders read in canon numbers.
84
85---
86
87## 2. Terms
88
89| Term | Meaning |
90|---|---|
91| **(θ, z)** | canon surface coordinate: θ radians around, z metres from the sun cap (§1.7) |
92| **arc** | θ × R, metres around the circumference. (z, arc) is the unrolled plane, which is exact |
93| **elevation** | metres above the datum cylinder, i.e. *toward the axis* |
94| **feature** | one authored thing in the world file: a mountain range, a lake, a forest… |
95| **world compile** | the whole-world pass that runs on load and on each edit (§5.2) |
96| **tile** | one square-ish patch of the unrolled surface at one level of detail (§6.1) |
97| **field** | a function of (θ, z): elevation, surface material, tree density… |
98| **scatter** | discrete objects placed by a field: trees, buildings, rocks, ships |
99
100---
101
102## 3. The world model (schema v2)
103
104`content/geography.json` and `static/world.json` merge into **one file**, since a
105place and the terrain that makes it are no longer separate things. No backwards
106compatibility: a one-off script ports today's geography into v2 so the authored
107world is kept, and then both old formats are deleted.
108
109### 3.1 Shape
110
111> **As built in Phase 1**, the schema differs from the sketch below in one structural
112> way: every kind has the **same `geometry` field** (point, polyline, polygon or band),
113> and the kind-specific parameters sit beside it — not `area`, `shore`, `course` and
114> `centre` fields per kind. One field meant the map, the list, the editor and the
115> permalinks handle every kind one way. And each phase adds a kind's parameters only
116> when a generator reads them, so Phase 1's `Desert` has `upliftM` and `edgeM`, not
117> dunes; `src/world/schema.ts` is the current truth. The sketch stays as the target.
118
119```ts
120interface World {
121 version: 2
122 /** Changes every feature's randomness at once. Rarely touched. */
123 seed: number
124 ground: GroundSettings // the gentle base relief everywhere (today's "base" op)
125 features: Feature[]
126 /** Generated hamlets and villages between the authored places (§4.7). */
127 settlementDensity: number // per 1,000 km² of habitable land; 0 = only authored ones
128 weather: WeatherPlan // Tower's published schedule (§4.9)
129}
130
131interface FeatureBase {
132 id: string // permalink target; follows the name until pinned (D-012)
133 name: string
134 blurb: string // ships to readers
135 notes: string // never ships (D-011)
136 public: boolean
137 /** Written once at creation and mixed into the world seed (§5.5). Never derived from the id. */
138 seed: number
139}
140
141type Area =
142 | { type: 'polygon'; points: [theta: number, z: number][] }
143 | { type: 'band'; zFrom: number; zTo: number } // rings the world (D-027)
144
145type Feature = FeatureBase & (
146 | Mountains | Lake | River | Sea | Desert | Forest | Settlement | Route | Marker
147)
148```
149
150### 3.2 The concepts
151
152Every parameter below has a default, so a feature can be dropped on the map with
153nothing but its geometry and look reasonable immediately.
154
155```ts
156interface Mountains {
157 kind: 'mountains'
158 area: Area // footprint; relief fades to the ground at its edge
159 spine?: Polyline // optional main ridge line; peaks are generated along it
160 peaks: Peak[] // authored summits, held at exactly their elevation
161 generatedPeaks: number // extra unnamed summits per 100 km² (default 0.6)
162 ruggedness: number // 0..1, how sharp ridges and deep valleys are
163 erosion: number // 0..1, how far the fluvial pass cuts in
164 snowlineM: number // default 3000
165 treelineM: number // default 2200
166}
167interface Peak { name?: string; theta: number; z: number; elevationM: number }
168
169interface Lake {
170 kind: 'lake'
171 shore: Polygon
172 levelM: number | 'auto' // 'auto' = fill the basin to its lowest rim
173 depthM: number
174 wind: Wind // drives the waves (§4.2)
175}
176
177interface River {
178 kind: 'river'
179 course: Polyline // source first, mouth last
180 flowM3S: number // sets channel width and valley size
181 source: { elevationM: number | 'terrain' }
182 outlet: string | 'auto' // id of the lake / sea / river it ends in
183 valley: { widthM: number; depthM: number } | 'auto'
184}
185
186interface Sea {
187 kind: 'sea'
188 area: Area // the cap seas are bands; a sea can also be a polygon
189 levelM: number
190 depthM: number
191 wind: Wind
192 ships: { count: number; routes: 'ring' | 'coastal' | 'crossing' }
e18dad04 2026-09-30 Pavel Safronov193 ice?: { edgeM: number; fastM: number; // D-090: open water, pack from edgeM, fast from fastM
194 range?: { fromM: number; heightM: number } } // D-093: ice mountains at the cap
376f2ce6 2026-09-16 Pavel Safronov195}
196
197interface Desert {
198 kind: 'desert'
199 area: Area
200 dunes: { coverage: number; heightM: number; wind: Wind }
201 hills: { coverage: number; heightM: number }
202 plateaus: { coverage: number; heightM: number; terraces: number }
203 pastWater: { washes: number; saltPans: number; shorelines: boolean }
204}
205
206interface Forest {
207 kind: 'forest'
208 area: Area
209 composition: { conifer: number } // 0 = all broadleaf, 1 = all conifer
210 giants: boolean // a tall forest: sequoia-class trees (§4.6)
211 densityPerHa: number // mature stems per hectare, default 450
212 stands: Stand[] // planned concentrations (§4.6)
213 meadows: { coverage: number; clearings: Polygon[] }
214 paths: { density: number } // auto-generated trail network, 0 = none
215}
216interface Stand { area: Polygon; densityPerHa: number; conifer: number; ageYears: number; layout: 'natural' | 'rows' }
217
218interface Settlement {
219 kind: 'settlement'
220 size: 'hamlet' | 'village' | 'town' | 'station' | 'city' // the capital is 'city'
221 centre: { theta: number; z: number }
222 boundary: Polygon | { radiusM: number } | 'auto' // 'auto' sizes from `size`
223 roads: { arterials: number; pattern: 'grid' | 'radial' | 'organic'; blockM: number }
224 buildings: { coverage: number; storeys: [min: number, max: number]; peakAtCentre: boolean }
225}
226
227interface Route {
228 kind: 'road' | 'path'
229 /** Either drawn, or two endpoints routed automatically across the terrain. */
230 way: Polyline | { from: string; to: string }
231 widthM: number
232}
233
234/** Everything that is a label and not a landform: towns, embassies, landmarks, notes. */
235interface Marker {
236 kind: 'embassy' | 'tower' | 'landmark' | 'note' | 'region'
237 geometry: Point | Polyline | Area
238}
239
240interface Wind { directionDeg: number; speedMS: number } // 0° = deepward, 90° = spinward
241
242interface WeatherPlan {
243 /** The wind each body of water gets when nothing is scheduled. */
244 baseline: { target: string; wind: Wind }[]
245 events: WeatherEvent[]
246}
247interface WeatherEvent {
248 id: string
249 target: string // a sea, lake or desert id
250 kind: 'calm' | 'breeze' | 'gale' | 'storm'
251 start: { day: number; hour: number }
252 durationH: number
253 wind: Wind
254 /** When Tower announced it. A plan is only a plan once people can read it. */
255 publishedAt: { day: number; hour: number }
256 /** Tower changing its mind. The published event is kept; this is what happens instead. */
257 revisions: { at: { day: number; hour: number }; kind: WeatherEvent['kind']; wind: Wind; reason: string }[]
258}
259```
260
261### 3.3 What changes from v1, and why
262
263- **Relief parameters move onto the feature.** v1 had `kind` on the place and an
264 `op` with its own parameter bag in a separate file. v2 has one discriminated union:
265 the kind decides what gets generated, and the TypeScript type decides which
266 parameters exist.
267- **Canyons and ridges stop being kinds.** A canyon is what a river valley is where
268 it passes through high ground; The Narrows becomes a named `Marker` on the
269 river's course. A ring ridge is a `Mountains` feature with a `band` area.
270- **Sub-elements live inside their feature.** Peaks inside mountains; stands and
271 clearings inside a forest. That is what makes them move, hide and delete together.
272- **Private features vanish from readers' terrain too.** A `public: false` lake is
273 stripped from the reader's file (D-011), so readers do not get its basin either.
274 That is the correct behaviour — a hole in the ground shaped like an unpublished
9d5a061c 2026-09-27 Pavel Safronov275 lake would leak it. Since D-072 the author uses the same app readers do, so an
276 unpublished feature exists only in `content/world.json` until it is published.
376f2ce6 2026-09-16 Pavel Safronov277
278---
279
280## 4. The concepts, one by one
281
282Each section says: what is authored, how it is generated, how it is drawn, and which
283libraries (if any) it uses. Library choices are justified in §7.
284
285### 4.1 Mountains — "reasonable peaks"
286
287**The problem with today's approach.** A dome with ridged noise laid over it,
288normalised to the declared height. It gives one summit at an arbitrary spot in a
289blister of noise, and no real ridges, saddles or valleys.
290
e7a9f9dd 2026-09-17 Pavel Safronov291> **As built (D-065)**, stage 3 is not an iterative erosion but the stream-power
292> **steady state**, computed directly over the drainage network and scaled back to the
293> authored massif; overlapping ranges erode as one group; and authored tarns keep a
294> shelf. The description below is kept as the plan; §15 says why each changed.
295
376f2ce6 2026-09-16 Pavel Safronov296**Generation, in three stages.**
297
2981. **A ridge graph.** The authored peaks plus `generatedPeaks` extra summits
299 (Poisson-disk placed, heights falling off from the spine and from the authored
300 peaks) are triangulated with **delaunator**. The minimum spanning tree of that
301 triangulation, weighted by the height drop along each edge, gives the main
302 ridges; a fraction of the remaining edges become spurs. Each ridge edge gets a
303 saddle height below the lower of its two peaks.
3042. **An uplift field.** Elevation is a function of distance to the ridge graph:
305 a sharp crest at the ridge (the arête), steepening flanks, and a foot that blends
306 to the ground at the edge of `area`. Authored peaks are pinned — the field
307 is exactly `elevationM` at each one.
3083. **Fluvial erosion.** The uplift field is sampled on a feature-local grid
309 (120 m; 200 m for a ring range, since a band is 314 km long) and run through the
310 **stream-power law with the implicit solver of Braun & Willett (2013)**, for a
311 fixed number of iterations proportional to `erosion`. This is what cuts
312 dendritic valleys and makes the ridges between them read as ridges. Authored peak
313 cells are held fixed during it. The result is cached (§5.4).
314
315**Below the grid**, detail comes from *derivative-damped fBm* (noise whose higher
316octaves are suppressed on steep slopes — the standard "eroded noise" trick),
317evaluated per tile.
318
319**Drawn as:** terrain tiles (§6). Material by elevation and slope: vegetation below
320`treelineM`, scree and rock above it, snow above `snowlineM` only where the slope is
321shallow enough to hold it (today's rule, kept).
322
323**Test:** every authored peak's generated elevation is within 1 m of `elevationM`,
324and is a local maximum within 500 m.
325
326### 4.2 Lakes — "waves"
327
328**Generation.** The shore polygon is carved into a basin whose depth profile follows
329distance from the shore. `levelM: 'auto'` fills to the lowest point of the rim, the
330way today's tarn op fills from the floor up.
331
332**The water surface is real geometry.** Level water in Tower is a surface of
333**constant radius** (§1.9.3), so a lake surface is a patch of a cylinder at
334`R − levelM`. That makes "the far shore rides 2.3 km above your tangent plane" true
335in the render for free.
336
337**Waves are sized from the lake, not chosen.** Wind waves are limited by *fetch*,
338the distance the wind blows over water. The JONSWAP fetch-limited relations give:
339
340- significant height `Hs ≈ 0.0016 · U · √(F / g)`
341- peak period `Tp ≈ 0.286 · (U / g) · (g F / U²)^⅓`
342
343A 10 m/s wind across a 30 km lake gives **0.9 m waves at a 4.2 s period**. A
344mountain tarn 2 km across gives 0.2 m. The shader is a sum of 4–6 Gerstner waves
345fitted to that height and period near the camera, fading to scrolling normal
346detail at distance. Shore foam where the water is shallow, from the depth between
347the water surface and the terrain under it.
348
349### 4.3 Rivers — "flow"
350
351**Canon first.** A Tower river exists only because the floor was shaped to make it
352descend (§1.9.3), and it runs in a closed pumped or weather-driven loop (§1.9.5).
353So the generator's one hard rule is: **the water surface descends monotonically from
354source to outlet.** If the terrain along the drawn course would make the river flow
9d5a061c 2026-09-27 Pavel Safronov355uphill, the valley is cut deeper until it does not — and the compile reports how
376f2ce6 2026-09-16 Pavel Safronov356much it had to cut, because that is information the author wants.
357
358**Generation.**
359
3601. The course is smoothed (Catmull–Rom, as today) and resampled every 200 m.
3612. **Long profile.** *Changed in Phase 1 (§15).* Not a concave curve between the ends,
362 and not clamped to the ground: the **least-earthwork** descending profile — the
363 weighted isotonic regression of the ground along the course, held to the outlet's
364 level at the mouth and a lake's level at a source in one, falling at least 2 m/km
365 so no reach is a canal. It cuts where the ground is high and **builds the ground up
366 where it is low**, in whatever balance moves the least earth.
3673. **Hydraulic geometry.** Channel width ≈ `a · Q^0.5` and depth ≈ `c · Q^0.4`
368 (Leopold's relations), so flow in m³/s sets the size. A tributary adds its
369 flow to the river it joins, downstream of the confluence. The Narrows gets its
370 392 m³/s from the four rivers that feed it, not from a number typed in.
3714. **Valley carving, and embankments.** Distance from the centreline carves a
372 channel, banks and a valley whose width depends on the ground: narrow where the
373 river crosses high ground (canyon), wide where it is low (floodplain). Where the
374 ground is below the profile, the same distance builds an embankment with 1:6.7
375 flanks. This replaces v1's hand-authored `reaches`.
376
377**Drawn as:** a ribbon mesh along the centreline, at the profile's water level,
378with UVs running along the flow. Flow is a normal map scrolled along the ribbon at a
379speed from Manning's equation, the technique of Three's `Water2` flow-map example.
380Where the profile is steep enough, rapids (white, faster); where a drop exceeds ~20 m,
381a waterfall sheet that **leans antispinward** by the impact angle
382`canon/motion.ts` already computes — 11° for a 1 km fall. The waterfall is the
383compass (§1.9.2), and this would be the first time the render shows it.
384
385### 4.4 Cap seas — "waves and sailing ships"
386
387**Generation.** A band carved into a basin, as today's flood op, with a water surface
388at constant radius across the whole ring.
389
390**A ring sea has unlimited fetch.** Wind blowing around the circumference crosses the
391same water forever, so the fetch limit of §4.2 never applies and the sea reaches a
392**fully developed** state: `Hs ≈ 0.21 · U² / g`, which is **2.1 m at 10 m/s and
3938.6 m at 20 m/s**. No lake on Earth does that; only open ocean does — and in Tower
394it happens only when Tower schedules it (§4.9). Wind speed at any moment comes from the
395weather plan, and the sea state follows it with a lag of a few hours, so a storm
396builds and a sea goes on running after the wind drops. Waves are Gerstner as in §4.2,
397with longer wavelengths and a swell component.
398
399**Ships.** `count` ships per sea, each with a route: `ring` circles the world
400along a constant z; `coastal` follows the shore; `crossing` shuttles between named
401harbours. Position is a **pure function of route and clock time**, so every reader
402at the same moment sees the same fleet in the same place. Ships **read the
403published plan**: a ship does not sail into a published storm, and waits in harbour
404until it has passed. Each ship samples the same wave function on the CPU at its hull to
405heave, pitch and roll.
406
407Hulls and sails are built procedurally (a lofted hull, a mast, one to three sails
408that are planes with a billow), a few hundred triangles each, instanced. Sails set
409to the sea's wind, so a ship on a ring route sailing against the wind tacks.
410
a088e5d5 2026-09-17 Pavel Safronov411*As built (§18): ring routes only, no harbours drawn, flat sails, and no tacking yet.*
412
376f2ce6 2026-09-16 Pavel Safronov413**Scale check.** A 40 m ship subtends 0.9 arc-minutes at 150 km, the edge of the haze
414horizon. At a 60° field on a 1,500 px-wide view one pixel is 2.4 arc-minutes, so it is
415under half a pixel: invisible to the naked eye there, a speck through the binoculars
416(the field-of-view control). At 20 km it is 7 arc-minutes, three pixels — a readable
417sail.
418
419### 4.5 Desert — "hills, plateaus, visible past water"
420
421**Generation, as layers inside the desert area:**
422
423- **Dunes.** Anisotropic ridged noise stretched perpendicular to the wind, with an
424 asymmetric profile (gentle windward slope, steep slip face). Tower makes its own
425 wind (§1.9.4), so the dune direction is a statement about what Tower does there.
426- **Rocky hills.** Low domes of ridged noise, `hills.coverage` of the area.
427- **Plateaus, mesas and buttes.** A noise mask thresholded into plateau shapes, then
428 raised by `heightM` through a **terrace function** with `terraces` steps: flat
429 tops, near-vertical cliff bands, talus slopes below. Small isolated parts of the
430 mask become buttes. The rock shader paints strata by elevation, so the layers line
431 up across separate mesas, which is what makes them read as one eroded formation.
432- **Past water — quoted, not remembered** (§0.1). Three marks, each switchable:
433 - **Dry washes** — a drainage network generated by the same flow routing as the
434 mountains (§4.1 step 3), cut shallow into the plateaus and the plain, with a
435 pale gravel floor and no water. Tower routes them as if water had carved them,
436 because that is what the originals it copied look like.
437 - **Salt pans** — the lowest closed basins, flattened and painted white.
438 - **Abandoned shorelines** — horizontal benches at fixed levels around a salt pan,
439 the bathtub rings of a lake that never existed.
440
441Because none of it is history, the generator is free to place these as **set
442pieces** — a salt pan ringed by shorelines where the author wants the view, not where
443a drainage model would demand one.
444
51c39d8b 2026-09-17 Pavel Safronov445*As built (§20): all of the above, with the pans placed in the lowest ground rather than
446by hand, and the terraces cut by the tiles rather than compiled.*
447
376f2ce6 2026-09-16 Pavel Safronov448### 4.6 Forest — "trees, planned concentrations, meadows, paths"
449
450**Trees are real size.** Conifers 20–45 m tall with narrow crowns; broadleaf 15–30 m
451with crowns ~0.3 of their height. Size varies with `ageYears` per stand and with
452elevation (smaller toward the treeline).
453
454**Where trees go is a density field**, in stems per hectare, built from:
455
4561. the forest's `densityPerHa`;
4572. **stands** — authored polygons with their own density, species mix and age.
458 These are the planned concentrations: Tower plants its forests, and a stand can
459 be `natural` (irregular) or `rows` (a plantation grid);
4603. **meadows** — authored clearings plus a generated fraction from a thresholded noise
461 field, with soft ragged edges;
4624. **exclusions** — water, roads, paths, buildings, slopes too steep, above treeline.
463
464**Placement is stratified, not a whole-domain Poisson solve.** The surface is divided
465into a fixed global grid of cells sized to the tree spacing; each cell hashes
466`(seed, cell)` to decide whether it holds a tree and where inside it, accepting with
467probability density / max density. This is *blue-noise-like*, and — the property
468that matters — **any tile can compute its own trees without knowing any other
469tile.** A sequential Poisson-disk algorithm cannot promise that (§7.3).
470
471**Paths.** A trail network connecting meadows, stands and the forest edge, routed by
472A* over a slope-cost grid and smoothed (§4.8). Paths clear trees and flatten a 2 m
473corridor.
474
475**Drawn as** three distances:
476
477| Distance | Representation |
478|---|---|
479| < 400 m | instanced meshes: trunk plus layered cones (conifer) or clustered lumps (broadleaf), 30–80 triangles |
480| 400 m – 4 km | instanced camera-facing impostors, one quad per tree |
481| > 4 km | no trees. The terrain shader paints canopy, and tile vertices in the forest are lifted by the local mean canopy height with a rough top, so a forest has a silhouette and an edge from far away |
482
483**The tall forest.** A forest with `giants: true` grows sequoia-class trees at
484**90–115 m** — coast redwoods reach 116 m on Earth, and hydraulics cap any tree near
485122–130 m (Koch et al., 2004), so Tower is growing them close to the limit of what a
486tree can be at 1 g. Stems are far sparser (≈ 40–80 per ha) and trunks 5–8 m across.
487
488Honest about what that looks like: the ordinary canopy is ~30 m, so the giants stand
489**~80 m proud of it** as a dark wall with a ragged top. That is 0.46° at 10 km —
490about a thumb's width at arm's length, clearly a landmark — and 0.09° at 50 km, a
491line on the horizon. Devils Tower is 265 m, so a giant forest on level ground is a
492third of that. If the author ever wants it to read at Devils Tower scale, the forest
493can stand on a raised plinth of ground (a `Mountains` feature of low ruggedness), and
494the two heights add.
495
496**Where it goes: near the capital, placed at random — once** (author, 2026-09-16). The
497port script creates it as its own `Forest` feature, **The Tall Wood** (provisional
498name), about 50 km² with a ragged outline. Its position is drawn from the world seed:
499on land, inside Greatwood, 15–50 km from the capital, and clear of The Deep, the
500capital's own boundary and its roads. The result is **written into the world file**,
501so it is placed once and then stays put for everyone; the author can move it like any
502other feature. Where it overlaps Greatwood, the Tall Wood's trees replace Greatwood's
503(the smaller forest wins inside its own area).
504
505**Why not render every tree.** Greatwood is 216 × 314 km. At 450 stems/ha that is
506**3 billion trees.** Only the ones within 4 km of the eye exist at any moment.
507
00ceeedb 2026-09-27 Pavel Safronov508*As built (§21): the density field, meadows, the three distances and the tall wood, with
509the ranges closer in than planned (meshes to 320 m, cards to 760 m, canopy skin beyond) and
510without authored stands or paths — stands and the trail network wait for phase 7's router.*
511
376f2ce6 2026-09-16 Pavel Safronov512### 4.7 Settlements — "blocky buildings and a few simple roads"
513
51c39d8b 2026-09-17 Pavel Safronov514*As built so far (D-070): a settlement is a **point with an extent** — a size and a radius,
515drawn on the map as a ring — and the Capital is 20 km across, centred on the axial midpoint,
516which reaches The Deep's shore. The layout inside it is this phase's work.*
517
376f2ce6 2026-09-16 Pavel Safronov518No usable city generator exists on npm (§7.3), so this is written in-house and kept
519deliberately simple, as asked. One generator handles every size; `size` sets the
520defaults:
521
522| Size | Radius | Buildings | Streets |
523|---|---|---|---|
524| hamlet | 150 m | 5–20 | one lane |
525| village | 400 m | 50–300 | a main street and a few lanes |
526| town | 1.2 km | 500–3,000 | arterials plus a grid or organic core |
527| station | 300 m | 20–80 | mountain settlements at the tarns (Sisters Station, Hollow Station); lifts and runs are a later addition |
528| city | 6 km | 20,000+ | the capital |
529
530**Filling out the world.** Beyond the authored places, the compile places generated
531settlements at `settlementDensity`. Candidate sites are scored by suitability — flat
532ground, fresh water within 2 km, not in the desert core, not above the treeline — and
533accepted largest first with a minimum spacing that grows with size. Generated
534settlements are **named for their size** — "Hamlet", "Village", "Town" — and marked
535as generated on the map (author, 2026-09-16). Several share a name, which is correct:
9d5a061c 2026-09-27 Pavel Safronov536nobody has named them yet. **Adopting** one — writing it into `content/world.json` as an
537authored feature — pins it: after that, changing the world elsewhere cannot move or
538remove it.
376f2ce6 2026-09-16 Pavel Safronov539
540Layout, for every size:
541
5421. **Arterials.** `arterials` roads from the centre to the boundary, bent to avoid
543 water and steep ground, and joined to any external routes that reach the city.
5442. **Local streets.** A `grid` (rotated to the main arterial), `radial` (rings plus
545 spokes) or `organic` (streets grown with branching and snapping) at `blockM`
546 spacing, clipped to the boundary and to the shoreline.
5473. **Blocks** are the faces of the planar road graph, inset by half the road width
548 (**clipper2-js** offsetting).
5494. **Lots** by recursive split of each block along its longer axis until lots are
550 15–40 m wide, keeping only lots with street frontage.
5515. **Buildings** are the lot inset by a setback, extruded to `storeys × 3.2 m`.
552 Height rises toward the centre if `peakAtCentre`. Flat roofs. Per-building colour
553 jitter. A city pad flattens the ground under the built area.
554
555The Spindle (3.2 km) stays a `tower` marker with its own mesh, not a generated
556building.
557
558### 4.8 Roads and paths — "around the capitol and through the forest"
559
560- **Drawn routes** are smoothed and draped on the terrain.
561- **Routed routes** (`{ from, to }`) run **A* over a cost grid**: cost rises steeply
562 with slope, water is impassable except at a bridge cost, existing roads are cheap
563 (so routes merge into networks rather than running in parallel). The result is
564 simplified and smoothed.
565- **Default network:** every settlement is joined to its nearest larger neighbour by
566 a road, and to the next nearest as well where that saves a long detour, so the
567 network is a connected web rather than a star around the capital. Hamlets get
568 tracks. Forests add paths (§4.6).
569- **Grading.** A route cuts and fills a corridor to a smoothed longitudinal profile
570 with a maximum grade (8% road, 20% path), blended into the ground over a shoulder.
571 Surface material is paved or packed earth.
572
573### 4.9 Weather plan — Tower's schedule
574
575Every wind in Tower is Tower's doing (§1.9.4). Tower **publishes its plans** so that
576people can live around them — sail, harvest, travel — and **changes them whenever it
577chooses**: for example, cancelling a storm because someone foolish has gone sailing
578into it. Both halves are data here, because both are story.
579
580- **Baseline:** each sea, lake and desert has a default wind when nothing is scheduled.
581- **Events:** calm, breeze, gale or storm on a target, for a start and a duration, with
582 the moment Tower published it.
583- **Revisions:** Tower's changes of mind, each with the moment it happened and a
584 reason. The published event is **kept**, so the app can show both what people were
585 told and what Tower actually did.
586
587**The clock gains a day.** Today the interior has a time of day only (D-053). Scheduling
588needs a day number, so the clock becomes `day + phase`. **Days count from 0, with no
589week or month names** (author, 2026-09-16). The panel shows the plan
590as Tower would publish it: a board of upcoming events, with revisions struck through
591and replaced.
592
593**What the weather drives:** wave height and direction on seas and lakes (§4.2, §4.4),
594sail trim and harbour decisions for ships, and the dune direction in a desert, which
595uses the target's baseline wind. What it does **not** drive: clouds and rain, which are
596still not drawn (D-049).
597
598**Default plan.** The port script writes a baseline breeze everywhere and a repeating
599pattern of a few scheduled storms per month on each cap sea, so the feature is visible
600before anyone authors an event.
601
a088e5d5 2026-09-17 Pavel Safronov602*As built (§18): the plan is written into `content/world.json` directly rather than by the
603port script, and deserts do not read it yet (Phase 5).*
604
376f2ce6 2026-09-16 Pavel Safronov605---
606
607## 5. Generation pipeline
608
609### 5.1 Overview
610
611```mermaid
612flowchart LR
613 W[world file] --> C[world compile<br/>workers, on load + on edit]
614 C --> K[(feature caches<br/>ridges, erosion grids,<br/>river profiles, city layouts,<br/>route polylines)]
615 C --> P[coarse world field<br/>~500 m]
616 P --> M[map view preview]
617 P --> X[exterior x-ray + far tiles]
618 K --> T[tile generator<br/>worker pool, on demand]
619 T --> R[interior renderer<br/>terrain · water · trees · buildings · ships]
620 T --> G[ground query<br/>standing, collision]
621```
622
623### 5.2 The world compile
624
625Runs in a worker on load and after every edit. It never produces pixels for the
6263D view; it produces the **expensive, whole-feature** results that tiles then sample.
627
628In dependency order:
629
6301. **Validate and index.** Parse, fill defaults, give every feature an influence box.
631 *Phase 1 checks the boxes directly: with 28 features that is cheaper than an index.
632 **flatbush** comes in when settlements and routes make the count large.*
6332. **Landforms.** Mountain ridge graphs and erosion grids; desert plateau masks and
634 wash networks.
6353. **Water.** Lake levels (`auto` needs the landforms), river long profiles
636 (needs outlet levels), river flow accumulation down tributaries.
6374. **Settlement.** Generated settlement sites, then layouts for every settlement;
638 route networks (need the final terrain cost grid).
6395. **Weather.** Resolve the plan's events and revisions into a wind timeline per target.
6406. **Coarse world field.** Elevation and surface material for the whole world at
641 ~500 m (1080 × 628), by evaluating the full elevation function once per cell.
642 This feeds the map, the exterior's x-ray and the most distant tiles.
643
644**Incremental.** Each result is keyed by a hash of the inputs that produced it.
645Editing a forest's meadows reruns nothing in steps 2–3; moving a peak reruns that
646mountain range, the rivers whose course crosses it, and the routes whose cost grid
647changed. Tiles are invalidated only where the changed features' influence boxes
648overlap them.
649
650### 5.3 The elevation function
651
652One pure function, used by the tiles, the coarse field, the ground query and the
653tests:
654
655```
656elevation(θ, z) =
657 ground(θ, z) gentle base relief
658 ⊕ landforms: mountains (max), desert (add) from their caches + detail noise
659 ⊖ basins: seas, lakes carved
660 ⊖ valleys: rivers, washes carved to their profiles
661 ⊜ grading: city pads, roads, paths cut and fill toward a target
662```
663
664Features are fetched from the spatial index, so a point in open plain evaluates
665the ground and nothing else.
666
667**Noise is evaluated on the cylinder itself.** 3D simplex noise at
668`(R cos θ, R sin θ, z) / scale` is seamless around the circumference by construction.
669That retires D-019's snapped-lattice workaround, which was a fix for sampling 2D noise
670on a surface that wraps.
671
672A sibling function, `surface(θ, z)`, returns material weights (rock, scree, snow,
673grass, meadow, forest floor, sand, gravel, salt, soil, paving) and the scatter
674densities (trees per ha, whether a building lot is here).
675
676### 5.4 Caching
677
678- **Memory:** an LRU of generated tiles (~400 tiles on desktop, ~120 on phones).
679- **IndexedDB:** feature caches keyed by input hash — erosion grids above all, since
680 they are the slow part (§8.3). A returning reader skips the compile's heavy steps.
681- **No cache is authoritative.** Clearing site data costs time, never correctness.
682
683### 5.5 Determinism, and the seed
684
685**The seed is part of the world file, so it is the same for everyone.** Nothing is
686rolled per visitor, per device or per page load. Two readers standing on the same spot
687see the same trees, the same village, and the same ship on the same wave; a reader's
688link lands on what the author saw.
689
690Where the numbers come from:
691
692| Seed | Set when | Changes when |
693|---|---|---|
9d5a061c 2026-09-27 Pavel Safronov694| `world.seed` | once, by the port script, to a fixed value | only if the author deliberately rerolls the whole world |
695| `feature.seed` | once, when the feature is written into the file — the one moment a random number is drawn — and saved with it | only if the author asks for that feature to be rerolled |
376f2ce6 2026-09-16 Pavel Safronov696| everything generated | never stored; recomputed from the two seeds above and the feature's parameters | whenever those change |
697
9d5a061c 2026-09-27 Pavel Safronov698`content/world.json` carries the seeds, and the `world.json` the app loads is made from
699it with the same seeds, so what the author sees is exactly what readers get.
376f2ce6 2026-09-16 Pavel Safronov700Placements made "at random" — generated peaks, generated settlements, the Tall Wood's
701position — are random in the sense of *chosen by the seed*, not *different each time*.
702
703- One integer hash (the existing `Math.imul` hash from `tools/noise.mjs`, kept) and a
704 seeded PRNG built on it. No `Math.random` anywhere in generation.
705- Every feature's randomness comes from `hash(world.seed, feature.seed)`, where
706 `feature.seed` is a number written once when the feature is created. It is **not**
707 derived from the id: ids follow names until pinned (D-012), so an id-based seed
708 would reshuffle a forest every time it was renamed.
709- A test generates a fixed set of tiles twice and compares them byte for byte.
710
711---
712
713## 6. Rendering in the interior view
714
715### 6.1 Terrain level of detail
716
717The single 512 × 768 cylinder is replaced by a **quadtree of tiles in the unrolled
718(z, arc) plane**, bent onto the cylinder.
719
720- **Level 0:** 16 tiles along × 10 around — 33.75 × 31.4 km each.
721- Each level halves the tile. **Level 10**, the finest, is 33 × 31 m.
722- Every tile is a **65 × 65 vertex grid** (33 × 33 on phones), so level 0 has 527 m
723 vertex spacing and level 10 has 0.5 m.
724- A tile splits when the eye is closer than **1.8** tile-widths (1.6 on phones),
725 measured to a sphere around the tile's *actual* relief, and is drawn from its parent
726 until all four children are ready — nothing pops *out*, detail pops *in*. Measured:
727 about 60 tiles a level and 740 chosen, of which frustum culling leaves about 200 draw
728 calls and 1.9 M triangles on a laptop.
729- Neighbouring tiles at different levels are sealed with **skirts** (a short curtain
730 hung from each edge). Geomorphing is a later refinement if popping is noticeable.
731- Around θ the tree wraps: tile index 9 neighbours tile index 0.
732
733**Precision.** Vertices are bent onto the cylinder **in the worker**, in coordinates
734relative to the tile's own centre, and the tile mesh is positioned at that centre.
735Float32 at 50 km in kilometre units is ~6 mm; relative to a tile centre it is
736sub-millimetre. Doing the bend in the vertex shader from absolute coordinates would
737put 6 mm of jitter on every tree and roof edge. The logarithmic depth buffer stays.
738
739**Standing on the ground** calls the same `elevation(θ, z)` function, so D-035's
740problem — the CPU and GPU disagreeing about where the ground is — disappears rather
741than being worked around.
742
743### 6.2 Surface shading without a bitmap
744
745The colour raster is gone. Each tile vertex carries material weights from
746`surface(θ, z)`; the fragment shader blends a small palette of procedural materials
747(colour, noise-driven variation, slope darkening) — splatting, with the "texture"
748computed rather than loaded. Distant tiles average to the same colours, so there is
749no seam between near and far.
750
751Every new material — terrain, water, trees, buildings, ships — includes
59c12827 2026-09-28 Pavel Safronov752`HAZE_GLSL`, `SUN_GLSL` and the shared uniforms (`uExposure`, `uSun` …), so haze, the
753sun and night apply to all of them uniformly (D-074). This is a hard rule: a material
376f2ce6 2026-09-16 Pavel Safronov754without it will glow through 200 km of air.
755
756### 6.3 Water, scatter, ships
757
758- **Water bodies:** each lake and sea has its own tile set at constant radius, drawn
759 only where the terrain is below the level (the depth test does the shoreline).
760- **Rivers:** ribbon meshes per tile.
761- **Trees, buildings:** one `InstancedMesh` per tile per model; tile visibility is the
762 culling. Upgrade path: `@three.ez/instanced-mesh` if per-instance culling is needed.
763- **Ships:** one instanced mesh per sea, updated per frame from the clock.
764
765### 6.4 Flying
766
767The interior already has eye height to 25 km and the drag-the-ground gesture
768(D-059). Added:
769
770- **Keyboard flight:** W/S forward/back, A/D strafe (arrows too), Q/E down/up, shift
771 to boost; speed is 1.2 × height above ground per second, at least 2 m/s, and shift
772 multiplies it by five. Climbing is a fixed factor per second — ×2 a second, ×7.4 with
773 shift — since the same ×5 there would take 100 m to 40 km in a single second.
774- Terrain clearance: the eye never goes below ground + 1.7 m.
a088e5d5 2026-09-17 Pavel Safronov775- **Scenic tour** (§19): a scene every thirty seconds, flown by the view itself.
3e898b51 2026-09-28 Pavel Safronov776- Eye height ceiling raised toward the axis (~45 km; since D-084, the beam's edge at 48.4 km) so the whole world can be seen
376f2ce6 2026-09-16 Pavel Safronov777 from the middle of the air, if wanted.
778
779---
780
781## 7. Libraries
782
783### 7.1 Adopt
784
785| Package | Version | License | Use | Why this one |
786|---|---|---|---|---|
787| **simplex-noise** ✅ Phase 1 | 4.0.3 | MIT | 2D/3D simplex for every field | TypeScript, tree-shakable, takes a seeded random function; 3D is what makes noise on the cylinder seamless |
e7a9f9dd 2026-09-17 Pavel Safronov788| **delaunator** ✅ Phase 2 | 5.1.0 | ISC | mountain ridge graph; city road graph faces | the standard, fast, tiny; updated 2026 |
376f2ce6 2026-09-16 Pavel Safronov789| **flatbush** | 4.6.2 | ISC | spatial index of feature influence boxes | static packed R-tree, ideal for "which features touch this tile"; transferable to workers as one buffer |
790| **clipper2-js** | 1.2.4 | Boost | polygon offset (road insets, setbacks), clipping (blocks to boundary and shore) | offsetting is the operation needed, and `polygon-clipping` does not offset. **Check its bundle cost before adopting**; the package ships 1.8 MB unpacked, mostly sources |
791| **comlink** ✅ Phase 1 | 4.4.2 | Apache-2.0 | worker RPC | removes the message-passing boilerplate for the worker pool; 4 KB |
792
793Three.js itself supplies `ShapeUtils` triangulation (earcut) and the `Water2` flow-map
794technique, so neither needs a separate package.
795
796### 7.2 Considered, useful later
797
798| Package | Why not now |
799|---|---|
800| `@three.ez/instanced-mesh` 0.3.16 (MIT) | per-instance culling and LOD; tile-level culling is enough to start |
801| `three-mesh-bvh` 0.9.15 (MIT) | fast raycasting; only needed if picking against generated meshes |
802| `@thi.ng/poisson` (Apache-2.0) | Poisson sampling with density functions — but sequential over a whole domain, which breaks per-tile independence (§4.6). Fine for placing generated peaks in the compile |
803
804### 7.3 Looked for and not found, or not suitable
805
806- **City / town layout.** Nothing on npm. Watabou's Medieval Fantasy City Generator is
807 Haxe and GPL-3.0. `garden-city` on GitHub is MIT and structurally close (road graph
808 → blocks → buildings) but unpublished, 0 stars. Write our own (§4.7), borrowing
809 ideas only.
810- **Erosion.** No npm package. The GitHub projects found are demos (WebGPU, particle
811 erosion) rather than libraries. Braun–Willett stream power is ~200 lines and
812 deterministic, which a droplet simulation is not. Write our own.
813- **Terrain LOD.** `@interverse/three-terrain-lod` (MIT, 2026) is a quadtree for a
814 *flat* plane with a single heightmap; `three.terrain.js` generates small
815 heightmap terrains. Neither handles a cylinder, per-tile worker generation, or
816 a surface that wraps. `@mapbox/martini` builds adaptive meshes but assumes a
817 square 2ᵏ+1 grid per tile with no crack handling between LODs. Write our own
818 quadtree (~500 lines).
819- **Road routing.** `ngraph.path` and `pathfinding` need an explicit graph or grid
820 object; for a million-cell cost grid a typed-array A* with a binary heap in a
821 worker is smaller and faster. Write our own.
822- **Ocean.** Tidewater and Three.js Water Pro exist, but target WebGPU/TSL or are
823 commercial, and none knows a water surface is a cylinder. Gerstner waves in our own
824 shader, including the haze.
825
826---
827
828## 8. Performance budgets
829
830### 8.1 Frame (desktop, 60 fps target)
831
832| | Budget |
833|---|---|
834| terrain tiles drawn | ≤ 250 (≈ 1.05 M vertices at 65²) |
835| trees, meshes | ≤ 60 k instances within 400 m |
836| trees, impostors | ≤ 400 k within 4 km |
837| buildings | ≤ 50 k in view |
838| draw calls | ≤ 600 |
839
840### 8.2 Generation
841
842| | Budget |
843|---|---|
844| one tile, all layers | ≤ 60 ms in a worker |
845| workers | `hardwareConcurrency − 1`, capped at 6 |
846| first usable view after load | ≤ 3 s (coarse tiles), full detail near the eye ≤ 8 s |
847| edit → visible change | ≤ 2 s for anything except a mountain range's erosion |
848
849### 8.3 The expensive one
850
851Erosion on the Girdle's ring at 200 m is 425 × 1571 ≈ 670 k cells; Braun–Willett at
852~100 iterations is estimated at 3–8 s in a worker. **When it is paid, exactly:**
853
854| Situation | Wait |
855|---|---|
856| first ever visit (nothing cached) | the full compile, **3–8 s** on a desktop, behind the status dialog (§10.1) |
857| returning visit, world unchanged | well under 1 s — the caches in IndexedDB are reused |
858| editing a mountain range (peaks, area, erosion) | that range's erosion again, 3–8 s for the Girdle, less for smaller ranges |
859| any other edit | ≤ 2 s; no erosion reruns |
860
861The author accepted this (2026-09-16), so precomputed erosion grids are **not** shipped.
862
863### 8.4 Phones
864
865**Rule: a phone sees the same world, not a thinner one. It sees less of it at once.**
866
867The forest's density, its stands and meadows, where every tree stands, every
868building and every ship are the same on a phone as on a desktop — they are authored
869canon, and a reader holding a phone in a meadow should be standing in the same meadow
870as the author. What shrinks is **reach and cost**:
871
872| | Desktop | Phone |
873|---|---|---|
874| tile grid | 65² | 33² |
875| trees as meshes | 400 m | 150 m |
876| trees as impostors | 4 km | 1.5 km |
877| canopy beyond that | yes | yes, same shading, so the forest's outline is unchanged |
878| workers | up to 6 | 2 |
879| ship wave sampling | all visible ships | within 5 km |
880
881So yes, a phone flies through a full-density forest; the individual trees simply start
882closer in. The tier is chosen automatically from the GPU and memory, with a
883**Detail: auto / low / high** override in the panel. A first compile on a phone is
884estimated at 2–3× the desktop time, 10–20 s, which the status dialog carries.
885
886---
887
888## 9. Views other than the interior
889
890- **Map.** Draws a hillshaded, material-coloured image of the **coarse world field**,
891 produced live by the compile, with feature geometry drawn over it as vectors. It is
892 still an image on screen, but it is a view generated from the features, not a
9d5a061c 2026-09-27 Pavel Safronov893 painted source of truth. The vectors are changed in the world file.
376f2ce6 2026-09-16 Pavel Safronov894- **Exterior.** The x-ray reading uses the same coarse field as its texture. Looking
895 in through the windows (D-060) uses only the coarsest tiles, since nothing finer is
896 resolvable through 100 km of glass and haze.
897
898---
899
900## 10. Editing
901
9d5a061c 2026-09-27 Pavel Safronov902*Rewritten for D-072.* The world is edited in **`content/world.json`**, not in the
903browser: the author describes a change, Claude makes it in the file, and the app
904regenerates from it. There is no writer mode, no local draft and no export. With
905`npm run dev` running, saving the file reloads the open page; the published site
906changes when the commit is pushed. The app loads `world.json`, which `vite.config.ts`
907makes from the file with every `notes` emptied and unpublished features left out
908(design.md §3.1).
376f2ce6 2026-09-16 Pavel Safronov909
9d5a061c 2026-09-27 Pavel Safronov910What each concept is authored as — the parameters a change touches:
911
912| Concept | Shape | Parameters |
376f2ce6 2026-09-16 Pavel Safronov913|---|---|---|
9d5a061c 2026-09-27 Pavel Safronov914| Mountains | polygon or band | peaks (name, position, elevation), generated-summit height and count, ruggedness, erosion, snowline |
915| Lake | polygon (the shore) | level (auto or a number), depth, basin, shore, rim, wind |
916| River | polyline, source to mouth | flow, source, outlet (by id, or auto) |
e18dad04 2026-09-30 Pavel Safronov917| Sea | band or polygon | level, depth, shore, wind, ships, ice (the ice edge and where the fast ice begins) |
9d5a061c 2026-09-27 Pavel Safronov918| Desert | polygon or band | uplift, edge, dunes, hills, plateaus, past water |
919| Forest | polygon or band | uplift, edge, density, conifer share, meadows, treeline, giants |
abc0aad0 2026-10-01 Pavel Safronov920| Farmland | polygon or band | edge (D-095); the fields are laid out from where they are |
9d5a061c 2026-09-27 Pavel Safronov921| Settlement | point | size, radius; a generated one is **adopted** by writing it in |
922| Weather | the plan's events | target, kind, start, duration, wind, when published, revisions with reasons; the cycle |
923| Road / path | polyline | width |
924
925Each change goes: file → compile (only what the change touches, §5.2) → map and
926interior regenerate. A change that does not touch the ground — a name, a blurb, a
927note, a wind — regenerates nothing.
376f2ce6 2026-09-16 Pavel Safronov928
929### 10.1 The status dialog
930
931Whenever a compile runs longer than 400 ms — always on a first visit, and after
932editing a mountain range — a dialog covers the view with a progress bar and one line
933saying what is happening, in the manner of Terraria's world creation. Shorter edits
934show only the small indicator.
935
936**Every message is a real step.** A line appears when that step of §5.2 starts, and it
937names the actual feature, so the dialog is honest progress rather than decoration.
938The voice is **Tower's**, since Tower is the one making all of it (§0.1). The author
939approved these lines (2026-09-16):
940
941| Step | Message |
942|---|---|
943| validate | Reading the plans |
944| mountain uplift | Raising {Snowcrown} |
945| erosion | Letting rain find its way down {the Girdle} |
946| desert | Quoting the deserts of Earth in {the Dry Reach} |
947| desert washes | Cutting riverbeds for rivers that never ran |
948| lakes, seas | Filling {The Deep} |
949| river profiles | Teaching {The Long Water} which way is down |
950| forest | Planting {Greatwood} |
951| giants | Letting {the tall wood} grow as tall as a tree can |
952| settlements | Choosing where people will live |
953| city | Laying out the streets of {the Capital} |
954| routes | Walking the roads in |
955| weather | Publishing the weather |
956| coarse field | Drawing the map |
957
958Steps that take less than a few frames still show their line briefly, in order, so the
959sequence reads as the world being built up rather than as a flicker.
960
961---
962
963## 11. Existing decisions this affects
964
965These need an explicit yes from the author before building, since some of them were
966deliberate choices:
967
968| Decision | Effect |
969|---|---|
970| **D-006** Interior fidelity: simple ground, "no scenery meshes, no settlements, no vegetation" | **Reversed.** Trees, buildings and ships are exactly that. `design.md` §4 also lists "procedural scenery, settlements" as explicitly out of scope |
971| **D-019** Geography authored as data, generated by `npm run terrain`, with no dependencies | **Superseded.** Still data-first, but generated live, and five runtime dependencies are added |
972| D-020, D-051 (the world's belts, the Girdle) | **Kept as content**, ported into v2 features |
973| D-021 (terrain tool progress) | Retired with the tool; progress moves to the UI |
974| D-024, D-047 (x-ray raster on the hull) | Kept, fed by the live coarse field |
975| D-035 (CPU height readback) | Retired: the CPU calls the elevation function directly |
976| D-039, D-057 (raster height range, no mipmaps) | Retired with the rasters |
977| `design.md` §2.4 "water has no surface" | Reversed: water is geometry |
978| D-049 (rain not drawn) | Kept. Wind and waves become scheduled weather (§4.9); clouds and rain stay undrawn |
979| D-053 (the day is a front) | Kept, and the clock gains a day number for the weather plan |
980| `design.md` §1.11 "The cap seas are not designed" | **Superseded by §0.1.** The seas pool where gravity puts them, but every landform around them is now a choice Tower made |
981| `test/raster.test.ts` | Deleted; replaced by generation tests (§13) |
982
983---
984
985## 12. Author's answers, and what is still open
986
987### 12.1 Answered (2026-09-16)
988
989| # | Question | Answer | Where it landed |
990|---|---|---|---|
991| 1 | "Visible past water" | Tower added these forms because it saw them on Earth and other planets and thought them appropriate — not because water was ever there | §0.1, §4.5 |
992| 2 | Ships and wind | Tower is in charge of the weather, publishes its plans so people can plan, and changes them when it wants — e.g. for a foolish sailor in a storm | §4.4, §4.9 |
993| 3 | Ring-sea storms | Up to Tower; it schedules them | §4.4, §4.9 |
994| 4 | Settlements | Generate them to fill out the world | §4.7 |
995| 5 | Tree scale | Real-world sizes, plus a tall forest of giants that reads as a landmark | §4.6 |
996| 6 | Compile wait | 3–8 s is acceptable behind a working dialog; Terraria-style status messages welcome | §8.3, §10.1 |
997| 7 | Phones | Left to the design: same world at full density, shorter draw distances | §8.4 |
998
999### 12.2 Answered (2026-09-16, second round)
1000
1001| # | Question | Answer | Where it landed |
1002|---|---|---|---|
1003| 1 | Tower's calendar | Days count from 0, no names | §4.9 |
1004| 1a | Is the seed the same for everyone? | Yes — it lives in the world file (the author's question; this was already the design, now stated explicitly) | §5.5 |
1005| 2 | Generated settlement names | Named for their size: "Hamlet", "Village" | §4.7 |
1006| 3 | The tall forest | Placed at random near the capital, once, then fixed | §4.6 |
1007| 4 | Status dialog lines | Approved as drafted | §10.1 |
1008
1009Nothing is open. The design is ready for Phase 1.
1010
1011---
1012
1013## 13. Testing
1014
1015Generation is pure TypeScript and runs under vitest without a browser:
1016
1017- **Determinism:** fixed tiles generated twice are byte-identical, and a world
1018 compiled in two separate workers from the same file produces identical caches.
1019- **Seams:** a tile's edge heights equal its neighbour's, at the same and at
1020 different levels, including across θ = 0.
1021- **Mountains:** authored peaks within 1 m of their elevation and locally highest.
1022- **Rivers:** water surface monotone non-increasing from source to outlet; ends at
1023 its outlet's level.
1024- **Water:** lake surfaces at constant radius; no lake level above its rim.
1025- **Forest:** no tree in water, on a path, in a clearing, or above the treeline.
1026- **Settlements:** no building overlaps a road or another building, or leaves the boundary.
1027- **Settlements:** generated sites respect minimum spacing; every settlement is
1028 reachable from the capital by road.
1029- **Weather:** a revision overrides its event from its moment onward and not before;
1030 no ship is at sea inside a published storm.
1031- **Routes:** grade never exceeds the maximum after grading.
1032- **Budgets:** one tile of each kind generates under its time budget on CI (loose).
1033- **Screenshots** (D-042): the tour gains views of each concept from the ground and
1034 from altitude.
1035
1036---
1037
1038## 14. Phases
1039
1040Each phase ends with something visible and runnable, documentation current, and a
1041screenshot check before any commit.
1042
1043| # | Phase | Delivers |
1044|---|---|---|
1045| 1 ✅ | **Foundation** | schema v2 + port script; status dialog; elevation function with ground + today's landforms reimplemented; worker pool; quadtree tile renderer on the cylinder; surface splatting; ground query; keyboard flight; map preview from the coarse field. Delete `tools/terrain.mjs`, the PNGs, `raster.ts`. **Parity checkpoint:** today's world, without the bitmap |
e7a9f9dd 2026-09-17 Pavel Safronov1046| 2 ✅ | **Mountains** | ridge graph, erosion, peak editing |
80bcc97a 2026-09-17 Pavel Safronov1047| 3 ✅ | **Water** | seas and lakes with surfaces and waves; rivers with profiles, flow and waterfalls |
a088e5d5 2026-09-17 Pavel Safronov1048| 4 ✅ | **Weather and ships** | weather plan with revisions and the day clock; plan board in the panel; procedural hulls, routes, harbour waits, heave on the waves |
51c39d8b 2026-09-17 Pavel Safronov1049| 5 ✅ | **Desert** | dunes, hills, terraced plateaus, washes, salt pans, shorelines |
00ceeedb 2026-09-27 Pavel Safronov1050| 6 ✅ | **Forest** | density field, stands, meadows, the tall forest, three-distance tree rendering |
376f2ce6 2026-09-16 Pavel Safronov1051| 7 | **Settlements, roads, paths** | one layout generator for every size, generated settlements and Adopt, routing, grading, buildings |
1052| 8 | **Editing polish, performance, phones** | incremental compile everywhere, IndexedDB caches, phone tier, screenshot tour |
1053
1054Phase 1 is the largest and riskiest: it changes how every pixel of the ground is made.
1055Nothing after it is started until its parity checkpoint looks right.
1056
1057---
1058
1059## 15. Phase 1, as built (D-063)
1060
1061What the plan did not know until the world was generated honestly:
1062
1063- **The rivers cross a lowland they cannot climb out of.** Between the Girdle's foot
1064 and the Dry Reach (z 115–135 km) v1's ground is about 60 m high, and the plateau
1065 beyond it 800–1,100 m. v1 painted its rivers across at ~1,000 m. The first profile —
1066 clamped to the ground — dropped every river to The Deep's 120 m and cut a canyon
1067 900 m deep through the whole desert; the second — a graded curve — needed a
1068 1,300 m embankment. The least-earthwork profile of §4.3 cuts The Narrows 300–500 m
1069 deep, which is what design.md §1.11 always said, and builds the lowland up by up to
14d35b7d 2026-09-16 Pavel Safronov1070 880 m under each river. That is the world as authored, generated faithfully.
1071 **Resolved by the author (D-064):** raise the lowland. The Uplands, a region lifting
1072 the ground to ~1,000 m, bring the worst embankment under 200 m; and the profile now
1073 prefers cutting to filling (a 0.2-quantile isotonic fit rather than least squares),
1074 since a cut reads as a valley and an embankment as a dam.
1075- **The hull was at the floor's radius.** Seen from outside, generated sea floors and
1076 tile skirts came through it as a lattice. The skin now sits at the wall's outer face,
1077 a kilometre out (D-044, D-064), and nothing generated may reach it.
376f2ce6 2026-09-16 Pavel Safronov1078- **Tiles were measured against the tallest mountain.** A fixed 4.5 km relief allowance
1079 made every tile within 5 km of the eye split to the finest level: 11,000 tiles on
1080 screen. Each tile is now bounded by its own measured relief.
1081- **Coarse tiles cannot show a river.** A vertex of a 500 m tile landing in an 80 m
1082 channel painted a 500 m blue diamond, in a dotted line down every river on the far
1083 side of the world. Water now weighs by how much of a vertex's spacing it covers.
1084- **The x-ray hull was mirrored end to end, before any of this.** v1's hull UVs flipped
1085 the axis, so the Girdle's snow sat at the far cap. It shows in the committed v1
1086 screenshot; fixed here.
1087- **Per-tile memory.** Normals travel as signed bytes, and every CPU copy of a tile is
1088 dropped once it is on the GPU.
1089
e7a9f9dd 2026-09-17 Pavel Safronov1090## 16. Phase 2, as built (D-065)
1091
1092Mountains are `src/terrain/mountains.ts`. What changed from §4.1 on contact with the
1093real ranges:
1094
1095- **Overlapping ranges are one group.** The Girdle and the four massifs standing on it
1096 are summits on one ring, triangulated, joined and eroded together; the three desert
1097 ridges, which overlap each other, are a second group. Separately eroded, the massifs'
1098 valleys would have met the ring's at a seam.
1099- **The ridges are blended, not maxed.** A hard maximum of cone-like flanks meets its
1100 neighbour along a straight crease and the range reads as folded paper. The flanks are
1101 combined by a soft maximum (the fourth root of the sum of fourth powers) and their
1102 distances nudged by noise.
1103- **Flanks are broad.** A 4,300 m summit's roots reach 27 km and it still stands 2,000 m
1104 high 12 km out — v1's massif size, and where its tarns and ski stations were.
1105- **Erosion is the steady state, not iterations.** Braun–Willett iterations over a
1106 smooth uplift field cut almost nothing in a sensible number of passes, and what they
1107 did cut ran in parallel grooves along the grid. The stream-power steady state can be
1108 written down directly over a priority-flood drainage network — each cell above its
1109 receiver by the slope that keeps pace with uplift — and eight passes, each re-finding
1110 the network, give branching valleys and sharp divides (Cordonnier et al., 2016). Three
1111 things were needed to stop the grid showing through: a surface roughened at valley
1112 scales to start from, a fresh jitter on the flood each pass, and a **channel
1113 threshold** of 1 km² with a little hillslope diffusion, below which ground is slope
1114 rather than a one-cell gully.
1115- **The steady state is scaled back to the author's shape.** Its heights have their own
1116 scale, so each cell is multiplied by the authored field over the steady state, both
1117 blurred over 8 km. The Girdle stays 2,900 m and Snowcrown's massif 4,300 m; the ridges
1118 and valleys inside them are the water's. `erosion` blends that with the uncut field.
1119- **Summits are the top, exactly.** Where anything within a few kilometres stands above
1120 a named summit the ground around it is scaled down smoothly; the summit is pinned to its
1121 elevation; a gentle cone trims the few cells pinning lifts past it; and it is pinned
1122 again. Fine detail is kept off the summit. Authored summits are exact to under a metre
1123 **above the datum** — the land under a summit is subtracted before it is pinned, where
1124 v1 added its 4,300 m on top of whatever ground was there — and higher than everything
1125 within half a kilometre (tested).
1126- **Tarns keep a shelf.** The steady state cut valleys through three of v1's four tarns.
1127 A cirque is ground the rivers did not cut down, so each authored bowl lake keeps its
1128 range at the uncut height around it. Sisters Tarn resolves to 2,173 m, beside the ski
1129 station design.md puts at 2,100 m.
9d5a061c 2026-09-27 Pavel Safronov1130- **Editing.** *(The editor was removed by D-072; summits are now edited in the file.)*
1131 Summits are listed in the editor with name and elevation, added by clicking
e7a9f9dd 2026-09-17 Pavel Safronov1132 the map and dragged to move; the range's generated-summit height, count, ruggedness,
1133 erosion and snowline are controls. Every change regenerates the range, and the service
1134 now runs **one compile at a time**, collapsing edits made during it into the next: a
1135 ten-step drag regenerated twice, not ten times.
1136- **Cost.** A compile is about 4 s in a browser worker for the whole world, nearly all of
1137 it the Girdle's erosion; tiles sample the 200 m relief grid and add detail at a few
1138 percent of the relief below it.
1139
376f2ce6 2026-09-16 Pavel Safronov1140Parity, against the committed v1 screenshots: the map matches in layout, colour and the
1141desert's ragged edge; standing at The Narrows and looking up from Greatwood match
1142within a few metres of elevation. Cold load of the production build to a fully detailed
1143interior is 4.3 s on the author's laptop; a compile runs in about 120 ms and the map
1144draws in about 1.2 s.
80bcc97a 2026-09-17 Pavel Safronov1145
1146## 17. Phase 3, as built (D-066)
1147
1148- **Water is a mesh per tile**, on the tile's own grid: every wet vertex carries its
1149 water's level, a dry vertex beside one borrows it, and the depth test draws the shore.
1150 The ground under drawn water is bed. A river narrower than a tile's vertex spacing is
1151 not drawn as a surface there, only as a tint on the ground in proportion to how much of
1152 each cell it covers — a coarse tile's vertex landing in a channel drew a 500 m blue
1153 diamond, and a line of them down every river.
1154- **Waves**: `water.ts` gives JONSWAP fetch-limited seas capped at Pierson–Moskowitz, and
1155 the tests pin §4.2 and §4.4's printed figures. Lakes and seas have a `wind`; the editor
a088e5d5 2026-09-17 Pavel Safronov1156 shows the sea it raises. Phase 4 made that wind the baseline under Tower's weather plan
1157 (§18).
80bcc97a 2026-09-17 Pavel Safronov1158- **Rivers**: speed by Manning, foam by grade. Ripples are advected downstream in the
1159 shader by a two-phase flow map.
1160- **Waterfalls come from the profile.** The least-earthwork fit pools reaches and steps
1161 between pools, so a step of 12 m or more is a fall. A river coming off a range stepped
1162 every 200 m for kilometres; consecutive steps are merged into one drop, with the reach
1163 below taking the foot's level — which cuts a **gorge**. The valley's walls are shaped
1164 from a separate *wall level* that ramps smoothly through the drop, so the gorge is a slot
1165 inside an unbroken valley; embankments use the same ramp, or they ended in a cliff the
1166 width of the embankment. Gorge walls stand at about 50°, wander with noise, and share the
1167 valley's curvature, or they cut a second cliff kilometres out.
1168- **Sheets lean antispinward** by `canon/motion.ts`'s free-fall drift, tested.
1169- **Skirts reach past a tile's steepest step.** On a gorge wall a coarser neighbour's edge
1170 can be off by far more than 4% of the tile, and the cracks showed as dark panels.
1171- **Rivers start and end at the water's edge.** v1 drew the Long Water from the centre of
1172 Hollow Tarn, and kept whole its channel was cut across the lake floor, its water a stripe
1173 across the tarn (reported by the author). A river is now trimmed where it meets a lake —
1174 found by sampling the water, since a tarn's water fills only about the inner 70% of its
1175 outline, and a trim to the outline left the river stopping short of it.
1176- **Narrow rivers stay visible at a distance.** Where a tile is too coarse to draw a river
1177 as a surface it tints the ground by the square root of the share of each cell the river
1178 covers: by the plain share, a 40 m river in a 200 m cell was a 20% tint, and vanished
1179 where tiles coarsened.
1180- **The beam is drawn after water**, or the far side's seas blended over it and it went
1181 fuzzy where it crossed them. Reported by the author.
1182- **The compiled world is cached** in IndexedDB (§5.4), keyed by the world's terrain
1183 signature and the worker bundle's URL — hashed in a build, so a new build never reads an
1184 old world. The screenshot tour compiles once and gives every shot the warm cache, and
1185 caps each page at two workers: it had been compiling the world fifteen times, four at a
1186 time, with a full pool each, and the author's laptop ran hot for it.
1187- **Cost**: 99–112 fps with water in view, 2.1–2.6 M triangles, on the author's laptop.
a088e5d5 2026-09-17 Pavel Safronov1188
1189---
1190
1191## 18. Phase 4, as built (D-067)
1192
1193- **The clock has a day.** The interior's clock is `day + time of day`, from day 0; the
1194 panel has a Day field, running the clock turns the calendar over at midnight, and the
1195 day and time are remembered across reloads. World time in hours is the one input the
1196 weather and the ships need.
1197- **The weather plan is data in the world file** (`weather: { cycleDays, events }`), and
1198 `world/weather.ts` reads it as pure functions of the plan and the hour:
1199 - **What Tower does** (`actualAt`): the event in force, as last revised *so far* — a
1200 revision counts from the moment it was made, and only within the occurrence it was
1201 made in. The wind rises over the event's first hour and falls over its last.
1202 - **What people were told** (`inHarbour`): the event as published, revisions ignored.
1203 Ships stay in harbour from three hours before a published gale or storm until it ends
1204 — including a storm Tower has quietly called off. That is the point of keeping both.
1205 A blow published with less than three hours' warning catches the fleet at sea, and it
1206 rides the weather out (changed in D-068; the default plan has one such squall).
1207 - **A repeating plan**: `cycleDays` repeats every event (28 in the default plan), so the
1208 seas stay eventful without authoring a year of storms.
1209 - **The sea lags the wind** (`seaWindAt`): waves answer an exponentially weighted memory
1210 of the last six hours of wind (2 h e-folding), so a storm builds and a sea runs on
1211 after it.
1212- **Waves are no longer compiled.** Phase 3 baked each body's sea state into the compiled
1213 world, which would have meant recompiling for every gust. Now a water vertex carries only
1214 its body's index, `terrain/sea-state.ts` computes every body's waves from the weather a
1215 few times a second, and the shader reads them from a small table. Wind and ship count are
1216 out of the terrain signature, so editing them no longer regenerates anything.
1217- **Whitecaps** cover the share of the sea Monahan and O'Muircheartaigh's fit gives for
1218 the wind, `3.84e-6 · U^3.41` — about 2% at 9 m/s, 9% at 20, 29% at 28. They break on
1219 two new short components (the wind's own sea on top of the swell); keyed to the whole
1220 sum they lined up along a storm's 600 m swell crests and left the troughs bare.
1221- **A precision fix, found through the storm.** The water shader measured position around
1222 the ring as `atan(y, x) × 50 km`, which in float32 is good to about 6 m: anything finer
1223 — whitecap noise, short waves, river ripples — smeared into streaks radiating from the
1224 eye. Surface coordinates are now measured from a reference point near the eye that moves
1225 in whole 2,457.6 m steps (256 cells of every noise scale used), and each wave adds back
1226 its phase at the reference, computed in float64. Distant long waves also fade out
1227 earlier (from 16 pixels of wavelength rather than 8), which removes a moiré of stripes a
1228 storm swell drew near the horizon.
1229- **Ships** (`terrain/ships.ts`): each ring sea has `ships` lanes (default 90), seeded from
1230 the world and the sea. A ship's position is a pure function of the hour: a steady 3–6 m/s
1231 round the ring, weaving gently in z, kept 4 km off either shore. Lengths run 8–45 m,
1232 mostly small. The hull, mainsail, jib and mast are procedural and instanced — three draw
1233 calls for every ship in the world; ships are drawn within 40 km. Each ship rides the same
1234 wave sum the shader draws (a TypeScript port, tested against its own derivative), heaving
1235 and pitching less the longer it is against the waves. The mainsail swings out running
e18dad04 2026-09-30 Pavel Safronov1236 before the wind and in toward the centreline heading into it. On a sea with ice the
1237 lanes keep to the open water, 2 km short of the ice edge (D-091).
1238- **Sea ice** (`terrain/ice.ts`, D-090, D-091): a sea's `ice` gives its ice edge and where
1239 its fast ice begins, as z; the fast ice runs on from there to the sea's end. Each water
1240 sample carries its ice cover, 0..1 — the pack from 0.3 at its edge to 0.95 at the fast
1241 ice, 1 on the fast ice — with the edge wandering 1.5 km and the fast ice's 400 m, by
1242 noise. The water shader breaks the pack into 110 m Voronoi floes, each ice or open by
1243 its own hash against the cover, with leads between and cracks where two floes meet; the
1244 fast ice is one sheet cracked in 660 m cells. Ice stills the waves under it. The map
1245 whitens the water by the cover.
abc0aad0 2026-10-01 Pavel Safronov1246- **Farmland** (`terrain/farms.ts`, D-095, D-097). A farm given as a point grows at compile
1247 time over a 200 m lattice: from its seed, always taking the frontier patch with the lowest
1248 key — half its distance from the seed over the farm's radius, half a hash — and never a patch
1249 that is wet, sand, rock, a range (relief over 150 m), ice, or steeper than 12%, until it has
1250 its `areaKm2`. The fields are a Voronoi of jittered **blocks** ~1.5 km across (a whole
1251 number round the ring) with 6 m tracks on their edges; in each block, a second Voronoi in
1252 the block's own frame — its own angle, field size 220–440 m and stretch 1.3–2.2 — measured
1253 from the block's centre so float32 holds; a third of those fields are cut into 2–4 strips.
1254 Each field takes a crop from a weighted table by hash; its rows run along its length. A
1255 field belongs to a grown farm if the patch under the point it was laid out from does. The
1256 same layout is written as GLSL, so the map and the views agree field by field. A sample
1257 carries only how much farmland it is, which clears forest; the ground shader draws the crop
1258 — rows over soil, bloom among the leaves, averaged once a row is under a pixel — and lights
1259 each field on its crop's own day from a uniform carrying the hours since sunrise.
e18dad04 2026-09-30 Pavel Safronov1260- **Ice mountains** (`field.ts glacier`, D-093): a sea's `ice.range` raises rock from the sea
1261 bed at `fromM` to `heightM` at the sea's end, steepening toward it (height ∝ t^1.6), ±30%
1262 round the ring and ridged by up to 0.7 of its height. Ice mantles it from where it leaves
1263 the water, as an ice sheet's plastic profile (τ = 150 kPa) to 350 m, draping 60% of the
1264 ridges; where a ridge stands out of the ice it is bare rock. Grounded ice is white however
1265 steep. The far cap's shader buries the glass's foot in ice to the height the ground stands
1266 at the cap, read from the terrain round the ring into a 2,048-sample strip.
a088e5d5 2026-09-17 Pavel Safronov1267- **Not built yet:** tacking (a ship on a lane into the wind sails straight into it, sail
1268 sheeted in), harbours (a ship in harbour is simply not on the water), ships on polygon
1269 seas and lakes, and weather over deserts (Phase 5).
1270- **The weather board** is a panel section in every view: upcoming occurrences for the
1271 next fortnight, the one in force highlighted, revisions listed under their event with the
1272 original struck through. Readers see only what has been published. Writers can schedule
1273 an event (published the moment it is added), revise one from now with a reason, delete
9d5a061c 2026-09-27 Pavel Safronov1274 one, and set the cycle *(removed by D-072: the plan is edited in the file, and the board
1275 shows only what has been published, to everyone)*. A selected lake or sea's readout shows its waves *now*.
a088e5d5 2026-09-17 Pavel Safronov1276
1277---
1278
99a2821b 2026-09-29 Pavel Safronov1279## 19. The scenic tour, as built (D-068, rebuilt by D-085)
1280
1281D-068 added a tour of nine thirty-second scenes, once the world had particular things in it
1282worth finding. D-085 replaced it, at the author's request, with the world's own flyers:
1283automatic flight, inside and out, a new craft every twelve to eighteen seconds, in an order
ec1a6075 2026-09-30 Pavel Safronov1284dealt at random, with the camera pointed at whatever is worth seeing. The caption's border
1285fills in clockwise as a leg runs and closes as it fades to the next (D-094).
99a2821b 2026-09-29 Pavel Safronov1286
1287- **A leg is a craft on a path, and a camera on the craft** (`tour/interior-legs.ts`,
1288 `tour/exterior-legs.ts`). Twenty-six legs inside and nine outside, each finding its
1289 subject in the compiled world — the tallest fall, the highest summit, the biggest ship at
1290 sea, a squall Tower gave no warning of, the broadest stand of mesas, the tallest giant, the
1291 sun where the clock has it — so they follow the world as it is edited, and a leg whose
1292 subject is gone drops out. The craft exists for its leg (D-085); only the ships are the
1293 world's own, followed where they are.
1294- **The craft**: paragliders, gliders, light aircraft and seaplanes, helicopters, airliners,
1295 airships, giant airships near the axis, trains, and outside, spacecraft (`views/craft.ts`).
1296 A winged craft banks into its turns by the lift the turn needs against the local gravity,
1297 so the same turn banks hard near the axis and barely at the floor; airships, ships and
1298 trains keep level.
1299- **Two rigs.** In the craft, looking where the director says, the horizon tilting with the
1300 bank when looking ahead; or chasing it, on the far side of the craft from what the
1301 director is looking at, so the two share the frame, walked slowly round it over the leg.
1302 A chase camera is never under 15 m over the ground.
1303- **The director** (`tour/director.ts`) scores every point of interest from the craft,
1304 twice a second: weight × transmission² × apparent size × how far off the nose, with the
1305 transmission integrated through the density profile as the renderer's haze is, and the
1306 sightline sampled against the ground for anything a hill could hide. The leg's subject is
1307 boosted fourfold. A subject holds the camera at least 4.5 s, bores it after 7, and a turn
1308 to the next is eased over 3 s and never swings faster than 0.7 rad/s. It is all worked out
1309 when a leg is first played, so a pose is a pure function of time.
1310- **Flights hold a steady clearance** over the highest ground within 700 m, smoothed, as
1311 D-068's did. Aim is taken in the tower's frame, where a straight line is straight: 6 km
1312 around, the floor has risen 360 m in the eye's frame.
1313- **Each leg sets the clock** and runs it at its own rate — real time, or a labelled
1314 time-lapse for the slow giants. The observer's own clock comes back when the tour stops;
1315 where they are does not, so stopping leaves them at the view they liked. Outside, the hull
1316 turns at the leg's rate whatever the Speed slider says.
1317- **Between legs the view fades to black and waits for the ground** — up to four seconds —
1318 so a leg opens on its terrain rather than on the coarse tiles of the moment
1319 (`views/tour-player.ts`).
1320- **The deal** (`tour/plan.ts`): every leg once before any twice, no two of a kind of craft
1321 in a row, two legs about the same subject kept apart where they can be, and a new deck never
1322 opening on the craft the last one closed on.
1323- **Controls**: *Scenic tour* in the interior's panel, and in the exterior's, starts and stops
1324 it; **N** skips to the next leg; **Esc**, a drag, the wheel or a flight key stops it where
42b4554e 2026-09-29 Pavel Safronov1325 it is, and so does switching to another view — a tour belongs to its view, and one left
1326 running behind another kept its caption on screen under the new one's.
99a2821b 2026-09-29 Pavel Safronov1327- **The screenshot tour photographs the legs**, each held at one moment
1328 (`tower.showScene(id, t)` inside, `towerOutside.showScene(id, t)` outside) and planned
1329 from a fixed seed, so a leg is the same flight every run. A test checks that every leg the
1330 screenshot tool names exists, and flies every leg over the real world, across twenty
1331 seeds while it was built: cameras stay in the world, above the ground and out of the beam,
1332 move and turn smoothly, keep a chased craft in the frame and bring each leg's subject into
1333 the middle of it; outside, nothing touches the hull or the beam's cone, and the grand shot
1334 keeps the sun clear of the collector.
51c39d8b 2026-09-17 Pavel Safronov1335
1336---
1337
1338## 20. Phase 5, as built (D-069)
1339
1340- **A desert is a grid and a pile of noise.** `terrain/desert.ts` compiles what a tile
1341 cannot work out alone — the plateau field, the wash network, the pans and their
1342 shorelines — on a 250 m grid over the desert (352,000 cells for the Dry Reach, about a
1343 second). Dunes and rocky hills are noise, and the tiles make them (field.ts).
1344- **The grid machinery is now shared.** `terrain/grid.ts` holds the grid, the priority
1345 flood, the box blur, the neighbourhood walk and bilinear sampling, which the ranges and
1346 the deserts both use; the ranges' copies moved there unchanged.
1347- **Terraces are cut as the tile samples, not in the compile.** The compile stores how far
1348 up the plateau each cell stands, smooth; `terrace()` turns that into flat tops and steep
1349 risers at whatever detail the tile has. Terraced in the compile, every cliff would be as
1350 wide as a grid cell — a 250 m ramp instead of a cliff band.
1351- **The plateau threshold is measured, not guessed.** The authored coverage is turned into
1352 a threshold by taking that quantile of the noise as sampled over this desert, and the
1353 same for the dune fields. Thresholds picked against an assumed spread put mesas over
1354 everything or nowhere — which is exactly what the first attempt did.
1355- **The washes are routed, not drawn.** The same priority flood the ranges erode with runs
1356 over the desert's own surface, and where a cell would drain more than 15 km² it is cut
1357 by 4 m and more, to tens of metres on the trunks. Two things this taught:
1358 - A flat desert has no drainage at all. The first version fanned out from every edge and
1359 gathered nothing, so **Tower crowns its deserts**: the middle of the Reach stands 150 m
1360 above its rim, and everything runs off it.
1361 - Widening a one-cell channel by blurring it averages the channel away. The wash is
1362 **dilated** to its full depth over a couple of cells first, and blurred afterwards for
1363 the banks.
1364- **Salt pans are set pieces in the low ground** (§4.5 allows exactly this): the lowest
1365 cells, spaced apart, each a disc flattened to one level with a crust that frays at the
1366 rim, and benches stepped every 20 m around it — the shorelines of a lake that never
1367 stood. Both are **levels in the world**, not in the sand: the shelf under the desert
1368 rolls by a hundred metres, and a floor that followed it would not be a floor.
1369- **Tower keeps its rock off its rivers.** A mesa field dropped across The Narrows left
1370 the whole world's drainage cutting a kilometre-deep slot through it. Rivers now carry a
1371 6.5 km corridor the plateaus keep out of, which is also what a river crossing a mesa
1372 field looks like. Beside the lower Long Water the desert's rock still stands over the
1373 river, and that section of canyon now runs about 780 m rather than 480.
1374- **Rock is a material, and so is salt.** Materials gained a ninth weight, `salt`, and
1375 bare rock is now claimed by the desert's plateaus, its gravel wash floors, and **any
1376 steep desert face** — sand does not stand at 60°. The ground shader lays **strata** by
1377 elevation, so the bands line up across separate mesas however far apart they stand,
1378 which is what makes a field of them read as one eroded formation.
9d5a061c 2026-09-27 Pavel Safronov1379- **The panel edits all of it** *(until D-072; now the file does, and the detail card
1380 keeps the readout)*: dune height, spacing, direction and coverage; hills;
51c39d8b 2026-09-17 Pavel Safronov1381 plateau height, coverage and terraces; washes, pans and shorelines — with a readout of
1382 what was actually made ("24% standing rock, 37% dune field, 4 salt pans").
1383- **The tour gained two scenes** (§19): past the mesas from the sand, and over a salt pan
1384 ringed by its shorelines.
1385- **Cost**: the whole world compiles in about 5 s, of which the Dry Reach is about 1 s.
00ceeedb 2026-09-27 Pavel Safronov1386
1387---
1388
1389## 21. Phase 6, as built (D-071)
1390
1391- **A forest is a density field and a placement rule** (`terrain/forest.ts`). Stems per
1392 hectare come from what Tower planted, thinned toward the forest's edge, opened where it
1393 left a meadow, and stopped by water, rock, sand and the treeline. The meadow threshold
1394 *and the width of a meadow's edge* are quantiles measured over the forest itself: a band
1395 of ±0.02 in noise units left Greatwood as scattered islands, because its noise only
1396 varies by about that much in total.
1397- **Placement is stratified, as planned.** A fixed global grid about one tree apart; each
1398 cell hashes its own coordinates for whether it holds a tree, where in the cell, and what
1399 it is, accepted in proportion to the density there. So **any tile, any view and any
1400 visitor works out the same trees with no knowledge of the others**, and walking away and
1401 back puts every tree back where it was. Tested.
1402- **Four ranges, not the three the plan drew** — because at three the wood visibly ended a
1403 few hundred metres off, and that edge walked along with you (reported by the author):
1404 - **to ~210 m — meshes.** A conifer is a tapered trunk and three cones; a broadleaf is a
1405 trunk and three lumps; a giant is a column with its crown in the top half. 40–70
1406 triangles each, instanced, 6,000 of them.
1407 - **to ~560 m — cards.** One quad each, turned to face the eye about the tree's own
1408 upright, with the silhouette cut out in the fragment shader: a cone, a round crown or a
1409 column. No texture — at that distance a tree is a few pixels and its outline is all of
1410 it that survives. About 39,000.
1411 - **to 2.8 km — clumps.** One card for everything in a 30 m cell, drawn as three crowns
1412 over a band: a card per tree would be a quarter of a million at a kilometre and a
1413 million at two, and a clump every thirty metres reaches nearly three kilometres for
1414 26,000. At that distance a clump is a thumbnail held at arm's length, which is what a
1415 group of crowns looks like from there.
1416 - **under all of it, from 620 m out — the canopy skin.** Each tile carries a second
1417 surface: its own ground vertices, lifted by the canopy's height with a rough top,
1418 sinking back onto the ground where the trees themselves take over. It is what closes the
1419 wood behind the clumps — a field of cards seen from above is all gaps.
1420- **The skin is opened up from below.** Seen from inside the wood it was a black ceiling
1421 over everything past the last drawn tree. A canopy is not solid — the gaps between crowns
1422 are what let you see any distance through a forest — so from underneath it drops about
1423 half its fragments on a pattern fixed to the ground, and what is left is lit as light that
1424 has come *through* the canopy rather than off it.
1425- **Meadows are grass.** The forest-floor colour now follows the stems, so a clearing
1426 inside a wood is the plain ramp and reads as open grass, which is what makes the meadows
1427 visible from the air at all.
1428- **The Tall Wood exists** (§4.6): 52 km² of giants, 90–115 m, drawn once from the world's
1429 seed — 17 km from the Capital, well inside Greatwood, clear of the water and the rivers —
1430 and written into the world file, where the author can move it like anything else. Its
1431 crowns are narrow for their height, as a redwood's are: at the planned 0.34 of height the
1432 grove closed over into unbroken dark, so a giant carries 0.18 and stands 18 m from its
1433 neighbours. **Greatwood gives up its ground where a smaller forest stands**, so the
1434 giants stand *instead of* the ordinary wood rather than mixed through it.
1435- **Cost**: 130–160 fps standing in Greatwood, and a rebuild is about 19 ms for 71,000
1436 trees. Three things bought that: the placement reads the density off the grid the view
1437 already samples rather than working the noise out per candidate tree; the clump layer,
1438 two kilometres out, is left alone until the eye has moved 170 m; and a box is cut to its
1439 circle before the ground is looked at. In flight the near budget drops to a quarter, and
1440 is restored in full the moment the eye settles — arriving somewhere used to leave the wood
1441 thin until you walked thirty metres.
1442- **The panel** sets stems per hectare, the conifer share, the meadows, the treeline and
1443 whether a wood is giants, and says what that comes to: "3.1 billion trees over 67,858 km²,
1444 about 4.7 m apart, canopy 28 m."
1445- **Two more tour scenes**: under one of the giants, and low over Greatwood's canopy.
1446- **A settlement clears its ground.** The Capital stands at z 270 km, in the middle of
1447 Greatwood's belt, and was standing in a wood; a forest now stops at any settlement's
1448 footprint and thins over its last fifth. Until phase 7 fills that footprint the Capital is
1449 20 km of grass, which is at least the right kind of nothing.
1450- **Not built:** authored stands (a polygon with its own density and species), paths
1451 through the woods, and trees on the map. Stands and paths belong with phase 7's router.