THE MAKING OF
How a four-paragraph brief became an explorable world: the decisions, the geometry, the simulation model, the code, the things that broke, and how to change it. Written for the next person who opens the files, which may be you in a year.
The brief
The request arrived as a prompt drafted in conversation with another model, and its core sentence was this: treat the human interior the way an advanced network engineer treats a complex network. Mostly invisible, composed of discrete objects with properties, failure modes and interrelations, and inspectable once you have a map. Make a high-fidelity, explorable 3D model of the classical Christian monastic map of the inner life, in a Tron-like network-visualisation style, that works both as a personal diagnostic tool and as a teaching environment.
The brief then listed what the map had to contain. The authors: Evagrius as foundation, Cassian, the Philokalia and the hesychasts, with Maximus, Climacus and the Desert Fathers as supporting voices, each in their own spatial region. The objects: the eight thoughts, the distinction between a thought arising and consent and passion, the passions, the nous, watchfulness, dispassion, fortresses, talking back, remedies matched to thoughts, and the question of external non-embodied amplification. A relational layer: how the thoughts feed one another, how the authors differ, which patterns hit beginners and which hit the advanced, how a habit becomes a stronghold. A playbook layer in plain 2026 language. And a metaphysical stance: psychology and spirit as overlapping circles, neither flattened into the other.
Then a single word was added in conversation: the key was fullness. And later: make it visually impressive, because that is what earns engagement.
Reading “fullness”
Fullness could have meant a bigger scene. It was read instead as two complete layers that had to be true to each other.
The object model
A structured inventory in which every faculty, thought, stage, remedy, structure, influence, author, text and concept is a record with the same fields: plain description, longer account, signs from inside, what strengthens and weakens it, playbook, scriptural replies, how each author treats it, beginner versus advanced weight, the two readings, sources. This is the “well-documented network”.
The world
A renderer that lays that inventory out in space, draws every relation as a line, and adds the dynamics the brief asked for: moving thought-forms, density that changes when a pattern is fed, watchtowers, fortresses. Nothing in the world is authored by hand; it all comes from the model.
The decision that followed from this is the most important one in the project: content lives in one file, data.js, and everything else is derived from it. The 3D world, the inspector cards, the tours, the diagnostic, the legend, and now the Reader’s Guide are all generated from the same records. Change a sentence in the model and it changes everywhere. This is also why the world could be built in a day: the engine is general, and the content is the work.
Three further readings shaped what got built:
- Diagnostic means interactive. A map you only look at is a poster. So the eight thoughts hold live state (a charge), the user can feed, starve, disclose and talk back to them, a checklist recolours the world to the user’s own patterns, and entering a thought shows where in the pipeline it sits in you.
- Teaching means guided. Eight tours fly a class from object to object with narration, a teach mode enlarges labels and hides the controls, and every state has a shareable link.
- Two circles means two readings on every object. Not a disclaimer at the top but a field on each record: a psychological account and a spiritual account, side by side, and an “external amplification” note that gives the tradition’s own tell for that thought.
The geometry of the map

Space had to mean something, so the layout encodes the model’s own claims.
- The centre is the person. Heart at the origin, nous above it, body below, the three powers of the soul on a ring of radius 11 around the heart, memory and imagination just off-centre, and the healthy states floating above.
- The eight sit near the power they infect. The ring of eight has radius 36. Gluttony, lust and greed cluster around the desiring power; sadness and anger around the irascible; vainglory and pride around the rational; acedia, which Evagrius says engages the whole soul, sits between. Their angles are set in the data, not computed, so the clustering is a claim you can edit.
- The practices sit between the person and the thoughts, on a ring of radius 23, because that is where they work.
- The corridor is a straight line from outside to the heart. It enters at 205° (between the Cassian wing at 180° and the hesychast wing at 270°), starts at radius 66 and ends at radius 10. The six stages are placed by a parameter
talong it; consent is att= 0.6, and the gate of consent from the Interior is drawn at exactly that point, so the two layers meet physically. The watchtower stands beside the gate; the coin-tester near coupling; the cell to one side. - The wings are hexagonal platforms of radius 40 at distance 130, one per compass point: Evagrius east, Cassian west, hesychasts north, supporting voices south. Inside each, the author stands at the centre, texts on a ring of radius 15, concepts on a ring of radius 28, and long grey arcs connect each wing object to the core objects it describes. Those arcs are hidden by default and appear when you select something, because with all of them on the map is a hairball.
- The outer field is a faint ring of dust at radius 120–190. External suggestion sits at the corridor’s far end; self-generated thoughts a little to the side; grace directly above the heart, sending rays down. Cassian’s three sources, in three places.
Visual identity
The brief said Tron, and Tron was taken literally: a dark ground, a grid floor, everything drawn as light. But the palette does work rather than decoration.
The eight thoughts are coloured by the power of the soul they infect, so the colour of a node is a claim from Praktikos 89 rather than a style choice. Each wing has its own hue (amber, violet, mint, pink) and its concepts inherit it, so a line from a wing into the centre tells you at a glance who is speaking. Green always means a remedy; gold always means a built structure or, in the guide, an advanced note.
Type is Rajdhani for headings and labels (a squared, slightly technical face that reads as instrument lettering without looking like a game) and IBM Plex Sans for reading, with Plex Mono for code in these documents. Labels in the world are canvas sprites with a dark rounded plate so they stay legible over glow.
Every node type has its own body: the heart is a pulsing sphere inside two wireframe shells; the nous an octahedron; the three powers are rings; the eight are dodecahedral crystals on hexagonal seats; stages are standing rings the thought passes through; practices are capsules; states are small warm stars with orbiting rings; authors are pillars with a bright cap; texts are slabs with text-lines that always face you; the watchtower has a lamp and a rotating beam; the fortress node is a walled hexagon. And every one of the eight carries a hidden fortress of its own, a hexagonal wall that scales up from the ground as its charge passes 45% and stands complete at 90%.
The glow is a bloom pass. It made three things wrong at first and each taught something. Labels rendered through bloom smeared into fog, so labels now live on a separate camera layer that is drawn after the bloom pass without it. Bright cores blew out to white, so emissive intensities were cut roughly in half and exposure lowered. And the second pass wiped the first: a Three.js scene with a colour background force-clears on every render call, so the label pass has to null the background before it draws and restore it after.
The object model
data.js exports seven things: the regions (WINGS), the three powers (PARTS), the node types (TYPES), the edge kinds (EDGE_KINDS), the nodes, the explicit edges, the tours, the diagnostic statements, and the list of the eight. A node looks like this, abbreviated:
{
id: 'acedia', type: 'logismos', wing: 'ring',
angle: 92, r: 36, part: 'whole', order: 6,
name: 'Acedia', aka: 'akedia · the noonday demon · …',
plain: 'Nothing here is worth it. …',
long: ['Evagrius (Praktikos 12) gives it his longest description. …', '…'],
signs: ['Checking the time, the phone, the window.', '…'],
strengthens: ['Sadness', 'Anger at the present', '…'],
weakens: ['Staying (hypomone)', 'Manual work', '…'],
remedies: [{ kind: 'ordinary', text: '…' }, { kind: 'spiritual', text: '…' }],
antirrhesis: [{ ref: 'Ps 27:14', text: 'Wait for the Lord; …' }],
voices: { evagrius: '…', cassian: '…', hesychast: '…', others: '…' },
feeds: ['gluttony', 'sadness'],
hits: { beginner: 0.5, advanced: 0.95 },
circles: { psych: '…', spirit: '…' },
external: 'The tell Evagrius gives: …',
sources: ['Evagrius, Praktikos 12, 27–28; Antirrhetikos VI', '…'],
}
Layout is a hint on the record, resolved by the engine: an explicit pos, or angle/r/y around the origin, or ring/slot inside a wing, or t along the corridor. Most relations are derived rather than listed: a thought’s feeds produces feeding edges, a practice’s remedyFor produces remedy edges, a wing object’s contributes produces the grey “describes” arcs, and each wing’s author is linked to everything in the wing. A short explicit EDGES list adds what cannot be derived: which power each thought is seated in, the healthy ascent, the corridor’s stage links, what enters where, and what the structures guard.
| Count | What |
|---|---|
| 86 | nodes: 6 faculties, 3 powers, 6 states, 8 thoughts, 6 stages, 4 structures, 14 practices, 3 influences, 8 authors, 7 texts, 21 concepts |
| ≈ 270 | edges, most derived |
| 8 | tours, 52 narrated stops |
| 25 | diagnostic statements |
The choice of which of Evagrius’ sequences to encode as feeding edges is the most interpretive part of the model. Gluttony → lust and vainglory → pride are explicit in Evagrius; greed → anger and sadness is Praktikos 18 and Cassian; sadness → acedia and anger → sadness follow both; acedia → gluttony (distraction snacks) and lust → vainglory (pride after victory) are readings of Praktikos 8 and 12. They are all editable, and the world redraws.
The simulation
The dynamic layer had to make the tradition’s claims visible without pretending to be a model of a mind. It is a toy with honest rules.
- Each of the eight has a charge in [0, 1], baseline 0.12. Charge decays toward baseline at 0.4% per second, or 1.2% with the prayer on, faster the higher it is.
- Thought-forms spawn at a rate of about one per second plus 1.6 per unit of total charge (0.4 with amplification off). Each picks a source (outside, memory, or occasionally from above), flies to the corridor mouth, and walks the stages in sequence.
- Its target is drawn from the eight with probability proportional to 0.04 + charge². That square is the whole “feeding the cat” model: a pattern with twice the charge is four times as likely to be fed again. The lens multiplies this by the pattern’s beginner or advanced weight.
- At the gate of consent it is intercepted with probability equal to the watchfulness level, times 0.85 unless the prayer is on. Interception flashes at the gate, brightens the watchtower, and writes a line in the log naming the thought and its source.
- A thought that passes goes on to passion, then to the heart, then a spark travels from the heart to its target and adds 0.05 charge.
- Feed by hand adds 0.25 and sends 35% of that down every feeding chain as a moving particle. Starve subtracts 0.20 and lights the remedy lines. Disclose halves the charge. Talk back subtracts 0.15 and shows one of the thought’s scriptural replies.
- Visually, a thought’s scale grows with charge, its glow with charge, its ambient traffic with charge, and its fortress rises smoothly between 0.45 and 0.9. Inside a thought, the stage ring reads the charge off at 0.2, 0.35, 0.5, 0.62, 0.75 and 0.9.
What the simulation deliberately does not claim: that thoughts are literally external, that charge is a measurement of anything, or that the numbers are calibrated. What it does claim, because the tradition does: that attention and consent are food, that what is fed comes back, that interception at the gate is cheap and demolition of a fortress is expensive, and that disclosure works.
The engine
Four files, no build step, no framework. Three.js 0.164 is loaded as ES modules from a CDN through an import map, which also gives access to its post-processing add-ons for the bloom. Everything else is plain browser JavaScript and CSS.
| File | Lines | Role |
|---|---|---|
data.js | ≈ 1,160 | The object model. All content. |
world.js | ≈ 970 | Layout, geometry per node type, edges as curves, the particle system, the simulation, camera and controls, picking, the inspector, tours, diagnostic, URL parameters, the render loop. |
index.html | ≈ 150 | The page: panels, modals, HUD, intro. |
style.css | ≈ 180 | Styling for the page chrome. |
build-docs.mjs | ≈ 250 | Generates the Reader’s Guide from the model. |
A few of the engineering choices worth knowing about before editing:
- Edges are quadratic Bézier curves lifted upward by a fraction of their length, so long arcs from the wings pass over the centre instead of through it. Every moving particle in the world is a position on one of these curves, which is why the same curve object serves drawing, ambient traffic, corridor walks and remedy sparks.
- All particles are one draw call. A single
Pointsobject with a small custom shader (soft round points, additive blending, per-point size) holds up to 2,400 thought-forms; the JavaScript side keeps an array of {curve, t, speed, colour, onArrive} and writes positions into the buffer each frame. TheonArrivecallback is how a thought walks the corridor stage by stage and how a strike delivers charge. - Picking uses invisible spheres. Each node has a transparent sphere slightly larger than its body, so clicking near a thin ring or a small capsule still selects it.
- Camera moves are wall-clock tweens between a position and a quaternion. They started out frame-time based, which is fine on a GPU and useless under software rendering, where a frame can take a second.
- Labels are a second render pass on camera layer 1 with no bloom. Their scale is proportional to distance up to a cap, so they hold a roughly constant size on screen, and each label has a distance tier: the eight, authors, influences and structures show from far away; practices, concepts, texts and stages only when you are close or when they are linked to the selection.
- Selection dims everything unlinked. Lines not touching the selected node drop to a fifth of their opacity and unlinked labels fade, so the map answers “what is connected to this?” without a query.
- URL parameters set the initial state (
focus,enter,tour,scenario,nepsis,prayer,amplify,lens,teach,glow,nointro). They exist so that a teacher can send a link to a state, and they exist because screenshots under software rendering needed a way to reach a state without a mouse. - Orbit keys. Q and E rotate the camera around the selected object (or the heart) on the vertical axis, by request, replacing the original up/down binding, which moved to Space and C.
The build log
In order, with what went wrong. The whole build ran in one working session on the evening of 11 September and the small hours of the 12th, with the documents and repo the following evening.
Read the brief; decide the two layers
Object model first, world second, content in one file. Chose the compass layout, the corridor, and the simulation rules on paper before writing anything.
Write the object model
All 86 records with their fields, the tours, and the diagnostic. The first attempt to write it through the shell failed because the file exceeded the operating system’s argument-size limit for a single command (E2BIG, at about 150 KB); it was written through the editor tool instead.
Write the engine, the page and the styles
Geometry per node type, edges, particles, simulation, controls, inspector, tours, diagnostic, intro.
Fix the positions
The corridor collided with vainglory; moved it to 205° and re-seated the watchtower, coin-tester, cell, fortress and outer-field nodes around it.
First look: the wrong browser
The connected developer-tools browser could not create a WebGL context at all. Switched to a local headless Chrome with a software renderer (SwiftShader), which draws the scene correctly but manages only a handful of frames in thirty seconds.
Modals would not hide
The hidden attribute lost to the modal’s display:flex rule. One line: [hidden]{display:none!important}.
Add URL parameters
To reach states for screenshots, and because they are useful for teaching anyway.
Labels were a fog
Every label grew with distance and went through the bloom. Moved them to a second, un-bloomed render pass with distance tiers and near-constant screen size.
The world vanished
The new label pass cleared the frame: a scene with a colour background force-clears on every render. Null the background for the label pass, restore after.
Every fly-to looked the wrong way
The target orientation was computed with a plain object’s lookAt, which points the +Z axis at the target; cameras look down −Z. Computed it with the camera itself instead. This one bug had made every tour, focus link and enter view face away from its subject.
Too close, too bright
Fly-to distances widened, enter orbit widened, emissive intensities halved, exposure lowered, unrelated labels hidden when near, tour bar moved so it no longer overlapped the inspector.
Documents, orbit keys, repository
The Reader’s Guide generator, this document, the shared stylesheet, Q/E as orbit keys, and a git repository.
The Witness path
A lived sample laid over the map: the builder’s own route, twelve stops in the first person, each in three parts (what happened, what the tradition calls it, what to start noticing), drawn as a rose thread through the objects it touched. It is data, not engine: a WITNESS record beside the tours. The tradition shipped a map and a witness together (Evagrius’ chapters, Athanasius’ Life of Antony); this is the same pairing.
The Gate
The third thing the tradition ships beside a map and a witness: a practice. Thoughts arrive at the gate of consent one at a time as text cards walking the corridor; the person names and dismisses (1–8), lets in (Enter), or takes to the money-changer’s table (T), and does nothing at their peril. Sixty thoughts in GATE: each with old words in the manner of Evagrius’ Antirrhetikos, modern words, source, pattern, reply, Cassian’s four coin tests and a debrief note. A beginner watch of the loud bodily thoughts and plainly good ones; an advanced watch of the thoughts from the right, the ten counterfeits, and the true consolation placed before its false twin. The engine reuses the corridor curve, the interception flash, the strike sparks and the charge mechanics; the camera parks at the gate and movement locks. In teach mode each thought pauses just before the gate and R reveals it.
Verification and its limits
What was checked: the page loads with no script errors; the module resolves its CDN imports; the overview, a focused thought with a scenario applied, an entered thought, a tour in progress, the heart, and the watchtower with its beam on all render correctly under software WebGL; the inspector, charge bars, log, and tour bar populate; the diagnostic markup and legend generate. Each fix above was confirmed with a fresh render.
What was not checked, because the environment could not: frame rate on a real GPU, mouse look and keyboard flight, hover tooltips, the feel of the tweens, the bloom at full resolution, sound (there is none), and mobile touch. The first run in a real browser is the honest test for those. If something is off it is most likely a speed or distance constant, and every one of them is a named number near the top of the relevant function in world.js.
What was checked only against memory: the chapter references. They are given at every node and are, to the best of the author’s knowledge, correct pointers into Sinkewicz’s Evagrius, Ramsey’s Cassian, the Palmer–Sherrard–Ware Philokalia and the Luibheid–Russell Ladder, but no edition was open during the build. Anyone teaching from this world should verify a citation before quoting it.
Extending it
To add an object, add a record to NODES in data.js with a type from TYPES, a wing, a name, a plain description, and a layout hint. For a new practice give it angle, r: 23 and a remedyFor list; for a new concept in a wing give it ring: 2, a slot and a contributes list. The engine draws it, wires it, and the guide includes it on the next build.
To add a ninth thought (envy, say, following Gregory), give it type: 'logismos', an angle on the ring, a part, feeds, hits and antirrhesis, and add its id to EIGHT so the simulation and diagnostic count it.
To edit the Gate, change GATE.thoughts: each entry is a level, a kind (bad, good, counterfeit), a pattern, a source, the old and modern words, a reply, the four tests and a debrief note. Levels and their pace are in GATE.levels.
To add or edit a witness, edit the WITNESSES list: each has an id, title, subtitle, colour, a framing and closing paragraph, and stops of a node id plus three texts. The thread redraws through whatever nodes the stops name, the picker and the guide list it automatically. The second witness, an ordinary week with the details anonymised, was added after the map was used on a live problem: the tradition’s test of a map is whether it works on a Tuesday.
To add a tour, push an object with an id, a title and a list of {node, text} stops to TOURS. To add a diagnostic statement, push {text, w: {pattern: weight}} to DIAGNOSTIC.
To change the geometry, the constants are at the top of world.js: the corridor’s angle and radii, and in the wing layout the ring radii. Wing centres are in WINGS.
To regenerate the guide: node build-docs.mjs (or npm run docs). It reads data.js directly and writes guide.html.
To change the look, the type-to-geometry mapping is one switch in buildNode, colours are in TYPES, PARTS and WINGS, and the bloom parameters are one line near the top.
Running, deploying, versioning
It must be served over HTTP because ES modules do not load from file://. Locally:
cd ~/mekk/theworld
python3 -m http.server 8765 # then open http://127.0.0.1:8765/
On the Pi it is static files under the default nginx root:
ssh claude@192.168.10.125 'sudo mkdir -p /var/www/home/interior/docs'
scp index.html data.js world.js style.css guide.html making.html docs.css claude@192.168.10.125:/tmp/
ssh claude@192.168.10.125 'sudo cp /tmp/{index.html,data.js,world.js,style.css,guide.html,making.html,docs.css} /var/www/home/interior/ && sudo cp -r docs /var/www/home/interior/'
# http://raspberrypi.home/interior/
The world and both documents are also published as private artifacts with their own links, which is the easiest way to hand them to someone.
The project is a git repository. The first commit holds the model, the engine, the page, the styles, the two documents, the generator and this brief. To publish it to the Gitea on the Pi, create an empty repository there named theworld and add it as the remote:
git remote add origin http://raspberrypi.home:3000/tor/theworld.git
git push -u origin main
Sources and caveats
The content draws on: Evagrius, Praktikos, On Thoughts, Antirrhetikos and Chapters on Prayer; Cassian, Institutes V–XII and Conferences 1, 2, 4, 5, 7–10 and 13; the Philokalia, volumes 1 and 4, chiefly Hesychios, Mark the Ascetic, Diadochos, Philotheos, Nikephoros, Gregory of Sinai and Palamas; John Climacus, The Ladder; Maximus, Centuries on Love; Athanasius, Life of Antony; and the Sayings of the Desert Fathers.
Three interpretive choices are the author’s and should be read as such. The feeding chains between the eight are a synthesis of Evagrius and Cassian, not a list either of them wrote out. The beginner and advanced weights are judgments drawn from the tradition’s remarks about which thoughts attack whom, expressed as numbers so the world can use them. And the plain-language names (greed for avarice, the inner PR department for vainglory, the replay for anger) are translations for a modern reader, kept alongside the originals so nothing is lost.
A side note, The Nine Fortresses, reads the Enneagram’s nine passions through the world’s seventh stage: what each builds when fed, the sentence each wall says, and whether a system of types demolishes those walls or decorates them.
Where the tradition disagrees with itself (Evagrius’ order against Cassian’s; apatheia against purity of heart; the eight against the seven), the world keeps both and says who said which. That is the network engineer’s habit too: document the vendor’s claim and the observed behaviour separately.