DOOM as a Spectral Lattice System¶
"Can't stop the signal, Mal. Everything goes somewhere, and I go everywhere." — Mr. Universe, Serenity (Joss Whedon, 2005)
Signature epigraph of the spectral-research collection. The body of work — validated results and rigorous falsifications alike — was offered through conventional channels and dismissed as foolery. The math stands independently. The discipline since: ship every result, falsifications included, with full reproducibility and per-row provenance (the Mathematical Provenance Method). A corpus that publishes its own invalidations is harder to dismiss than one that doesn't, and propagates through every channel that ingests open research. The signal is in the world; it goes everywhere now.
Authors: Steven (mlehaptics Project), Claude (Anthropic), Gemini Code Assist Date: May 2026 Status: Active research — translating id Tech 1 into a graph-Laplacian spectral model. End-to-end existence proof for the chess-spectral Rosetta Stone procedure (see §7.4); anchored at v1.0.0.
Project navigation + state-pointer¶
ReadTheDocs landing — https://mlehaptics.readthedocs.io/en/latest/ — is the canonical pointer to the current state across all sister notebooks in this project.
This notebook is a snapshot. Future framework additions will not be back-ported into it; the RTD landing tells you whether new sister notebooks or downstream developments are available.
Brief since-summary (as of 2026-05-08): - ephemerides-spectral framework matured through v0.26.0 — per-body action-angle catalogues, Attested Multi-Source Collector framework with MPR v1 normative format, four-tier reproducibility model (T0 / T1 / T2 / T3), and a schema-gap-driven trigger. PyPI: https://pypi.org/project/ephemerides-spectral/. - The Mathematical Provenance Method (MPM) formalised as the project's discipline (ephemerides notebook §0.0); instrument-first physics critique in ephemerides §20 with explicit three-regime classification (impulse + ring-down / driven sustain / driven with irreversibility) that names the doom-spectral substrate as a regime-A/B/C-handling framework natively. - Inkscape contribution shipped on the
spectral-faithfulbranch — three new SVG filter primitives (feSpectralBilateral,feSpectralDistance,feSpectralNoise); the closest sibling to doom-spectral in spirit (real-renderer integration of the same eigenbasis substrate; see Inkscapemr_description.md). - mfo-spectral sister-notebook added (May 2026) — Metric Field Ontology, one candidate foundational-ontology framing hosted in the project (cavity-instrument analogy; fractal metric field; ~11D structure from gauge-group + spectral requirements). Not the project's endorsed answer over alternatives; ephemerides §20 cites MFO as a worked example without picking a spatial-structure side. Future MPM target. - Ephemerides §21 — Tool-rejection as MPM-screening failure (symmetric counterpart to §20). Names the evaluator-side screening failure: rejecting work by which tool was used to make it, not by what it claims. Anchored on the historical orbital-mechanics chain DE441 traces back to (Copernicus / Bruno / Galileo / Kepler). §21.3 names the disability-accommodation dimension explicitly: categorical tool bans function as participation barriers for contributors with aphantasia, ADHD, dyslexia, motor disabilities, and many other variations. MPM screens are tool-agnostic by design.
Authorship attribution¶
The notebook covers an arc that spans two AI collaborators with very different scopes of contribution; documenting which work came from where is the same authorship-discipline the chess-spectral notebook applies in §42.
- Steven (mlehaptics Project) — primary research direction across the whole arc; the music / resonance / haptic-manifold vocabulary that the framing is built on; all decision authority; hardware integration testing; design questions that drove the work forward at each turn ("can we make secrets glow," "wire a PCB jumper for the demo version mismatch," "scale the window without changing the res," "let's tag this v1.0.0 to trigger RTD stable"). The chess-spectral §42 methodology section explicitly credits this collaborator's vocabulary as the dictionary that enabled the cross-disciplinary translation; the doom-spectral integration is the first instance where that same dictionary was applied to a legacy game engine.
- Claude (Anthropic) — co-author on the integration arc and the capstone documentation. Specifically: §6 (the verbose live-integration walk-through into linuxdoom-1.10), §7 (the FPU → graph-Laplacian replacement procedure), the entire fix-branch that landed as PR #221 (every compile / link / run fix needed to take the original PR #220 vendor-drop from "won't compile" to a runnable 32-bit ELF), the TrueColor X11 translation layer, the IDSPECTRAL cheat with classic SCRAMBLE encoding, the graph-diffusion bleed (one-hop propagation along
ds_e1m1_adjso the secret-glow hum reaches sectors the player can actually see), the demo-version PCB jumper, the post-#220 review that identified the original integration bugs, the follow-up TrueColor scale-loop optimization (PR #222), and the v1.0.0 capstone tag annotation. - Gemini Code Assist — original five-track research sketch (§1, May 2026) and the first-pass linuxdoom-1.10 integration patches that landed as PR #220. The five tracks (Blockmap/Sector graph Laplacian, Z-axis fiber bundle, dynamic sheaf Laplacian, sound diffusion, BIP encoder kinematics) are the structural skeleton this notebook is built around; the initial vendor drop of the linuxdoom source tree at
doom-spectral/source/linuxdoom-1.10/is also from this contribution. Several specific math decisions (the 512-D BIP hypervector dimension, the coprime shift constants 67 / 7 for cross-axis decorrelation, the 85-sector E1M1 lattice extraction inds_data.h) originate here. The integration patches in PR #220 didn't compile on first land, but the data tables, the encoder shape, and the research-track decomposition were the right starting point for the fix branch to build on.
Living document. Sibling to: - ../chess-maths/chess_spectral_research_notebook.md — The static fiber bundle and piece kinematics; §20.21 frames this notebook as the fourth sibling and tabulates the Track 1 / Track 3 / Track 4 correspondences (2D Grid Laplacian; dynamic sheaf raycast; heat-equation diffusion). The chess notebook is the foundational one — it carries the full vocabulary stack and the cross-disciplinary methodological note (§42). - ../othello-maths/othello_spectral_research_notebook.md — The dynamic sheaf Laplacian (raycasting and LoS). Track 3 of this notebook is mathematically identical to Othello's §10.7 ray-flanking mechanic — the doom-spectral implementation reuses the algebra directly. - ./antikythera_spectral_research_notebook.md — same-folder sibling. The cyclic-group / Laplacian-eigenbasis structure read off the ca. 150–60 BCE Antikythera bronze; established the integer-ALU + cosine-LUT + Q-format discipline that this notebook reuses for the BSP / Sector graph. - ./ephemerides_spectral_research_notebook.md — same-folder sibling. The ALU-native \(Z_{2^{32}}\) BIP encoder and Phase-9 adaptive couplings shipped to PyPI as
ephemerides-spectral; this notebook is the first sibling that ports the BIP/Phase-9 machinery to a non-celestial-mechanics problem (a 1993 first-person-shooter map).
0. Framing: 1993 Carmack Meets 2026 Spectral Graph Theory¶
The original DOOM (1993) engine is a masterpiece of integer ALU-dominant engineering. Because 1993 CPUs lacked fast FPUs, DOOM relied on: - 16.16 Fixed-Point Arithmetic: Integer representations of continuous space. - BAMs (Binary Angle Measurement): Angles mapped to an 8-bit or 32-bit integer cycle (0-255 or 0-4294967295), where integer overflow handles \(2\pi\) wrap-around natively. - Precomputed LUTs: Sine and cosine evaluated via array lookups.
This is exactly the substrate of our ephemerides-spectral BIP (Bit-Interleaved Phases) encoder. This project translates the spatial and mechanical realities of DOOM into a physics-only hyperdimensional engine governed by graph-Laplacian spectral methods.
0.1 The 2.5D Assumption as a Fiber Bundle¶
DOOM maps are 2D planar partitions (the BSP tree). "Height" (floor and ceiling elevation) exists as a property of a 2D polygonal Sector. Mathematically, DOOM is a 2D base manifold with a scalar fiber. The topological impossibility of "room-over-room" in the original engine is the definition of a trivial trivialized fiber bundle.
1. Research Subagents & Implementation Tracks¶
To build this ALU-dominant physics engine, the research is split into five distinct subagents:
Track 1: The Base Graph (Blockmap & Sector Topology)¶
DOOM uses a 128x128 unit regular grid (the Blockmap) to optimize collision detection, laid over an arbitrary planar graph (Sectors and Linedefs). - Hypothesis 1.1: The Blockmap can be modeled exactly as a 2D Grid Laplacian, yielding the 2D DCT eigenbasis. - Hypothesis 1.2: Sectors act as a super-graph (a coarse-graining of the Blockmap). The restriction map between the Blockmap Laplacian and the Sector Laplacian defines physical boundaries.
Track 2: The Z-Axis Fiber (Elevation and Collision)¶
Elevation (Z) is not a 3rd dimension in the base graph; it is a fiber attached to the Sector graph.
- Physics Constraint: An entity can move from Sector A to Sector B if Floor(B) <= Z_entity + MaxStep and Ceiling(B) >= Z_entity + Height_entity.
- Spectral Mapping: This is a state-dependent phase gate on the edges of the Sector Laplacian. If the fiber criteria are unmet, the edge weight goes to 0 (a wall).
Track 3: Dynamic Sheaf Laplacian (Line of Sight & Raycasting)¶
Hitscan weapons (pistol, shotgun) and monster Line of Sight (LoS) require traversing rays across the map. - Othello Connection: This is mathematically identical to the Othello ray-flanking mechanic (§10.7 of the Othello notebook). - Implementation: A cellular sheaf where restriction maps along a ray evaluate to 1 (open air) or 0 (solid linedef / closed door). The dynamic sheaf Laplacian instantly identifies the first point of impact for any hitscan vector.
Track 4: Sound Propagation (Graph Diffusion)¶
In DOOM, when a weapon is fired, the sound travels from the origin Sector to all adjacent Sectors sharing a portal, waking up monsters. Sound does not travel by physical distance, but by topological flooding. - Spectral Mapping: This is exact heat diffusion on the Sector Graph. - Equation: \(S(t) = e^{-L_{sector} \cdot t} S(0)\). The spectrum of \(L_{sector}\) dictates exactly how sound permeates a map, identifying acoustic "bottlenecks" (choke points in the map design).
Track 5: ALU-Native Kinematics (BIP Encoder)¶
Entities moving through the map have momentum and inertia.
- Using the ephemerides-spectral integer ALU pattern, Velocity_X and Velocity_Y are mapped to modular phase additions over \(Z_{2^{32}}\).
- Wall sliding (sliding along a linedef when colliding at an angle) is the projection of the velocity vector onto the null space of the collision edge's normal vector.
2. Experimental Data / Ground Truth¶
We will use the original DOOM shareware WAD (DOOM1.WAD), specifically parsing E1M1: Hangar, to extract the ground-truth node topologies, linedefs, and sectors for our initial spectral constructions.
2.1 Track Implementations (Verified May 2026)¶
The five foundational research tracks have been materialized into Python reference implementations and verified via integration_test.py:
- Topology & Diffusion (
research-doom/doom_topology.py): Extracted theL_sectorgraph Laplacian and mapped sound propagation to exact matrix exponential diffusion (the Heat Equation over the sector super-graph). Verified that sound decays physically across topological distances. - Kinematics (
research-doom/doom_kinematics.py): Replicated John Carmack's BAM (Binary Angle Measurement) fixed-point movement model using strictly bitwise modular math on \(Z_{2^{32}}\), including wall-sliding projections. - Dynamic Sheaf Hitscan (
research-doom/doom_sheaf.py&research-doom/doom_raycast.py): Implemented a Bresenham-based 1D raycaster that models Line of Sight as a directed sheaf Laplacian, where restriction maps act as phase gates (open air = 1.0, wall = 0.0). - Z-Axis Fiber Bundle (
research-doom/doom_fiber.py): Modeled 2.5D physical constraints (floor step-up, ceiling clearance) as state-dependent edge severing on the 2D sector graph. Height is mathematically confirmed as a scalar fiber. - WAD Geometry Mocking (
research-doom/wad_parser_mock.py): Scaffolded a baseline topology (inspired by E1M1 Hangar) with floor/ceiling elevations for spectral testing.
2.2 Real-World Ground Truth: E1M1 Hangar (Verified May 2026)¶
Using wad_parser.py to extract the original 1993 id Tech 1 geometry from DOOM1.WAD, we verified the spectral models against E1M1: Hangar.
| Metric | Real E1M1 Result | Interpretation |
|---|---|---|
| Sector Count | 85 | Total topological nodes in the base manifold. |
| Portals (Edges) | 100 | Total two-sided linedef connections. |
| Sound Reach (t=2.0) | 18 Sectors | Acoustic reachability from Sector 0 (Start). |
| Z-Fiber Step-Up > 24 | 24 Portals | 24% of edges represent "windows" or "ledges" requiring lifts/jumps. |
| Player Connectivity | 39 Portals | Only 39% of connections are traversable by Player 1 (Z=0, Step=24). |
| Algebraic Connectivity | 0.012923 | Extremely low \(\lambda_2\) confirms a linear "corridor-based" expansion. |
| Spectral Radius | 11.024394 | Max eigenvalue of the Laplacian. |
3. Results & System Integration¶
The components were integrated and validated against both mock and real WAD data:
- 3D Movement Denial: The Z-Fiber successfully severs edges in the Sector Laplacian. In real E1M1 data, it identifies that over 60% of visible portals are physically impassable from a neutral \(Z=0\) state.
- Sheaf Ray Absorption: Hitscan rays correctly absorb at the first grid cell where the sheaf restriction map is \(0.0\).
- Topological Diffusion: Sound propagation via \(e^{-Lt}\) correctly identifies the acoustic reachability of sectors. The 18-sector reach in E1M1 matches the "cascading" layout of the initial hangar complex.
4.1 Phase-9 BIP Kinematics & Breathing Couplings (Implemented May 2026)¶
We transitioned kinematics from 16.16 fixed-point to a Phase-9 BIP (Bit-Interleaved Phases) encoder.
- Encoding: Coordinates \((x, y)\) are mapped to a 512-dimensional hypervector \(H \in \{-1, 1\}^{512}\) via coprime cyclic shifts (67, 7).
- Breathing Couplings: To mirror the Ephemerides HDC (Jupiter-Saturn resonances), we introduced non-linear interaction energy between the X and Y axes. The cyclic shift is perturbed by a "breathing term" \((x \cdot y \bmod 512)\). This introduces harmonic resonances into the spatial manifold, causing the phase space to bend slightly based on geometric coordinates.
- Spatial Orthogonality: Distant points in E1M1 (e.g., \((100, 200)\) vs \((500, 800)\)) show near-zero similarity (dot product \(\approx -0.015\)), while adjacent points maintain a detectable correlation.
- Result: Spatial progression is now represented as a trajectory in high-dimensional phase space, allowing for collision detection via dot-product thresholding.
4.2 Spectral BSP Partitioning (Implemented May 2026)¶
The level's spatial hierarchy was automatically derived using Spectral Clustering (Fiedler vector partitioning) rather than heuristic geometric splits.
- Primary Cut: The Fiedler vector (\(\lambda_2 = 0.012923\)) split E1M1 into two nodal domains of 39 and 46 sectors.
- Bottleneck Identification: The spectral split severed only 3 portals, identifying the absolute geographic "choke point" of the Hangar's layout.
- Application: This method allows for the automated generation of BSP trees that are topologically optimized for sound and visibility propagation.
4.3 Physical Sound Diffusion (Refined May 2026)¶
Replaced the unstable high-order Taylor expansion with a stable multi-step Euler integration of the Heat Equation:
- Model: \(s(t+\Delta t) = s(t) - \Delta t \cdot L \cdot s(t)\).
- Implementation: 8-step iterative diffusion in doom_spectral.c.
- Result: Sound intensity remains strictly bounded within \([0, 1]\), providing a physically correct "spectral flooding" signal that naturally obeys the level's topological bottlenecks.
4.4 The Haptic Manifold: Phase Anchors (Implemented May 2026)¶
To drive the mlehaptics hardware, we implemented a sensory layer that translates topological state into motor tension.
- Phase Anchors: Each sector is assigned a 512D "Anchor Hypervector" \(H_{anchor}\). These anchors are diffused across the graph to maintain local phase correlation.
- Spectral Tension: The "friction" experienced by the player is calculated as: \(Tension = 1.0 - \text{Similarity}(H_{player}, H_{anchor})\).
- Engine Trace: The DOOM engine now outputs real-time
[HAPTIC]tension signals in the player loop, enabling high-fidelity sensory feedback based on the player's resonance with the environment.
4.5 Sheaf Raycasting & AI BIM Awareness (Implemented May 2026)¶
We have fully replaced DOOM's heuristic Line of Sight (LoS) and sound-alert logic with pure spectral operators.
- Sheaf Raycasting (
p_sight.c): Integratedds_sheaf_raycastas a fast-path inP_CheckSight. The raycaster models LoS as a directed sheaf Laplacian, absorbing spectral signals at topological boundaries. - AI BIM Awareness (
p_enemy.c): ReplacedP_NoiseAlertflood-fill withds_calculate_monster_awareness. Monsters now "wake up" based on the topological resonance (dot product) between their local Phase Anchor and the diffused sound field \(S(t)\).
5. Summary of Artifacts¶
| Artifact | Location | Description |
|---|---|---|
| BIP State | results-doom/e1m1_bip_sample.npy |
512D hypervector of a sample E1M1 coordinate. |
| Partition Map | results-doom/e1m1_spectral_partition.npy |
Fiedler vector and sector cluster assignments. |
| Haptic Anchors | results-doom/e1m1_haptic_anchors.npy |
85 sector Phase Anchors for sensory feedback. |
| WAD Parser | research-doom/wad_parser.py |
Binary IWAD extractor for id Tech 1 data. |
| Headless Runner | research-doom/ds_headless |
Compiled manifold simulator with rich CLI args. |
| Spectral Engine | doom-spectral/source/linuxdoom-1.10/ |
DOOM source code with integrated spectral lattice. |
| Runnable Build | doom-spectral/source/linuxdoom-1.10/linux/linuxxdoom |
32-bit ELF; runs against shareware doom1.wad on Ubuntu/WSLg with TrueColor X11 + IDSPECTRAL cheat. See §6. |
6. Live Integration into linuxdoom-1.10 (May 2026)¶
This section is deliberately verbose so a reader doesn't need the linuxdoom-1.10 source open to follow along. The integration is small in line-count (~250 inserted lines across 9 engine files) but each hook is sitting on top of an engine subsystem with its own contract, and the design choices only make sense once you see those contracts.
6.1 What Got Built (and the Order It Got Built In)¶
The integration was developed as a single fix-branch (fix/doom-spectral-integration-build)
on top of the original PR #220 vendor-drop of linuxdoom-1.10. The
arc was:
- Make it compile. PR #220 had commented out every engine
include from
doomdef.h, leaving every dependent.cfile blind to the engine's basic types. Restored the basic-type includes (doomtype, m_fixed, m_swap, tables, d_event, g_game, dstrings, sounds) plusdoom_spectral.hordered between them; left the subsystem aggregate headers (doomdata, p_mobj, d_player, d_items, d_net, p_tick) commented out because they re-include doomdef and trigger a header cycle onmapthing_t. - Make it link. The integration sites referenced
ds_get_haptic_tension,ds_sheaf_raycast, andds_calculate_monster_awareness-- all defined in the standalone reference atresearch-doom/c/src/doom_spectral.c, none copied into the integrateddoom-spectral/source/linuxdoom-1.10/doom_spectral.c. Copied the missing function bodies in. Fixed a Bresenham y-step sign bug from the reference (dy = -|by1-by0|was the intent; the original had two wrong-sign branches). Added the 85×512ds_e1m1_anchorstable thatds_get_haptic_tensionindexes. - Make it portable. linuxdoom-1.10 is a 1997 32-bit Linux target. Two classes of breakage on a 64-bit host:
- Pointer-truncation in WAD overlay structs (
maptexture_t::columndirectorywasvoid**, 4 bytes on x86_32, 8 on x86_64; throws the subsequentpatches[]array out of WAD-on-disk alignment by 4 bytes and SIGSEGVs inR_InitTextures). Locked toint32_t. - Pointer-array undersizing:
Z_Malloc(numtextures * 4, ...)assumes 4-byte pointer width; on 64-bit it allocates half the needed bytes and writes past indexnumtextures/2corrupt the Z_Malloc heap. Replaced every such call withZ_Malloc(N * sizeof(*arr), ...). Same fix inr_data.cfor textures, columnlump, columnofs, composite, etc. - Strategic decision: 32-bit build. Rather than chase every
pointer-width issue through linuxdoom's renderer / sprites /
visplanes (the entire reason Chocolate Doom and PrBoom+
re-ported the renderer),
Makefilegot-m32 -L/usr/X11R6/liband the 64-bit fixes above are now belt-and-suspenders against a future 64-bit retry. - Make it run on a 2026 X server. linuxdoom-1.10 only supported
8-bit PseudoColor visuals (1997 SVGA-style displays). Modern
X servers (Xorg, WSLg, XWayland) only offer 24/32-bit TrueColor.
Built a "translation layer" in
i_video.c: try PseudoColor first, fall back to a 24-bit TrueColor visual; the engine still paints 8-bit indices intoscreens[0], butI_FinishUpdatenow expands those to 32-bit BGRA via a per-palette LUT (X_palette_lut[256]) beforeXPutImage. Pure ALU expansion -- one indexed byte load + one 32-bit store per pixel + optional block-replication for-2/-3/-4window scaling. (Detailed walk-through below in §6.3.) - Make the demo loop survive a stock IWAD. Stock
shareware/registered DOOM IWADs ship demo lumps tagged at
VERSION 109; linuxdoom-1.10 is VERSION 110. Strict bail with
"Demo is from a different game version!" wedges the title
screen on every IWAD that ships in the wild. PCB-style jumper
in
g_game.c::G_DoPlayDemo: accept any version, log a one-line note when it isn't 110, consume the byte unchanged. Same fix Chocolate Doom applies for the same reason; the tic-command stream is byte-compatible 109↔110. - Make the spectral integration not crash the engine. The
original
p_map.cZ-fiber gate readds_e1m1_adj[a][a](a sector's diagonal in its own adjacency matrix == 0 by graph convention) on everyP_CheckPositioncall -- which fires on every player movement step including the overwhelming majority that stay inside the same sector. So the player got trapped in the spawn cell. The hook also read STATIC sector heights fromds_e1m1_sectors[]-- a snapshot at WAD-extraction time. Doors, lifts, crushers all have heights that change at runtime; the static table never updates, so as soon as a door's static height said "closed" (ceiling==floor) the gate blocked the player from passing through it forever. Disabled the entire hook: the engine's ownP_CheckPositionalready validates Z properly via livesector_t::floorheight/ceilingheight; our static-snapshot version was actively wrong on dynamic geometry. Track 2 (Z-axis fiber bundle) remains a valid spectral primitive for offline analysis; it just shouldn't gate gameplay movement. - Make spectral state visible to the player. Added the
IDSPECTRALcheat, modeled on classic IDDQD/IDKFA. Toggles a sinusoidal pulse on every E1M1 secret-tagged sector + its one-hop graph-neighbors viads_e1m1_adj. Pulse intensity is driven by cosine-similarity between the player's BIP hypervector and the per-sector anchor; pulse phase isgametic-driven via a quarter-wave sin LUT. Pure ALU, no FPU. Detailed in §6.5.
6.2 The Module Boundary¶
doom_spectral.c is the only file that touches the precomputed
lattice tables in ds_data.h. It exports a thin C API the engine
calls into. Engine-side code never sees ds_e1m1_* directly; it
goes through API functions that fold in the registration check.
doom_spectral.{c,h} <- spectral primitives + lattice tables
▲
│ C API (no engine types crossed)
│
linuxdoom .c files <- engine code calls API at hook sites
p_user.c ds_bip_encode + ds_get_haptic_tension (devparm trace)
p_map.c (ds_fiber_can_traverse hook DISABLED -- see §6.1.6)
p_sight.c ds_sheaf_raycast (Bresenham stub; gated)
p_enemy.c ds_diffuse_sound + ds_calculate_monster_awareness
p_setup.c ds_set_current_map (E1M1 + 85-sector match required)
g_game.c ds_secretglow_pulse + ds_lattice_adjacent (IDSPECTRAL)
st_stuff.c cheat sequence + state toggle for IDSPECTRAL
This boundary is the same discipline ephemerides-spectral uses for
the C↔Python bridge: a small set of pure functions, no shared
mutable state crossing the boundary except through explicit setter
functions (ds_set_current_map, ds_secretglow_set_active).
6.3 The TrueColor Translation Layer (i_video.c)¶
This is the most architecturally interesting non-spectral piece, and worth documenting because it generalises to any 1990s-era 8-bit indexed renderer that needs to land on a 2020s TrueColor display.
DOOM renderer (unchanged)
│
│ writes 8-bit palette indices
▼
screens[0] <- 320×200 byte buffer (X_8bit_buffer)
│
│ I_FinishUpdate per tic
│ for each src pixel:
│ dst[i] = X_palette_lut[ src[i] ] (pure ALU)
▼
XImage (32-bit ZPixmap, depth 24/32)
│
│ XPutImage
▼
X server (Xorg / WSLg / XWayland)
Key properties:
- Renderer is unchanged. Every line of
r_*.cstill paints into an 8-bit indexed framebuffer. We don't touch the column drawer, sprite blitter, sky renderer, or any of the inner per-pixel loops. - Palette LUT is the only translation.
X_palette_lut[256]is auint32_ttable rebuilt byI_SetPalettewhenever the engine swaps the palette (gamma changes, REDPAIN flash, BONUSPIC tint, etc.). Each entry is(R << 16) | (G << 8) | Bpacked for a little-endian 32-bit BGRA XImage on x86_32. Pure integer ALU: per-frame work is 64,000 byte-loads + 64,000 32-bit-stores at 320×200, auto-vectorised by gcc -O2 if SSE4.1+ is available. - MITSHM disabled. WSLg advertises XShm but the segment isn't accessible across the WSL/Win32 boundary. A 64KB blit per frame doesn't need it.
- Block replication for window scale.
-2/-3/-4flags pump the X11 window to 2×/3×/4× resolution by writing each source pixel as an N×N block in the output, instead of letting the X server upscale (which would soften the classic crisp pixel look).
6.4 Map Registration Gate (ds_set_current_map)¶
The lattice tables in ds_data.h are E1M1-specific (85 sectors,
precomputed φ-vectors, Fiedler partition, anchor hypervectors,
adjacency matrix). On any other map, blindly indexing them would
assert-abort the engine. The registration API is the gate:
void ds_set_current_map(int episode, int map, int sector_count);
d_boolean ds_spectral_is_registered(void);
ds_set_current_map is called from p_setup.c::P_SetupLevel AFTER
P_LoadSectors populates numsectors, and only marks the lattice
"registered" when:
The triple check is deliberate: a custom WAD that uses the same
E1M1 lump tag but loads a different sector count must not engage
the lattice. Every spectral hook checks ds_spectral_is_registered()
before accessing per-sector data, so the entire integration becomes
a no-op on every other map / WAD.
6.5 IDSPECTRAL: From BIP Hypervector to Sector Lightlevel¶
This is the cleanest vertical slice of the spectral integration --
it goes from the player's (x, y) coordinate all the way to a
visible lighting effect, entirely through ALU primitives, with the
graph Laplacian doing real work in the middle.
Per-tic data flow:
player.mo->{x, y} (engine)
│
│ p_user.c per tic, gated on registered + console player
▼
ds_bip_encode(x, y, &player_spectral_state)
│ - quantise (x>>16) % 512, (y>>16) % 512 [coarse, see §0]
│ - 512-D bipolar HV from φ_x and φ_y row product:
│ h[i] = ds_phi_x[(i + ix*67) % 512] * ds_phi_y[(i + iy*7) % 512]
│ - coprime shifts (67, 7) for BIP cross-axis decorrelation
▼
player_spectral_state : ds_hypervector_t (512 × int8_t)
│
│ g_game.c::G_DoSecretGlowTick once per tic
│ for each sector i with (was_secret OR is_neighbor):
▼
ds_secretglow_pulse(i, phase)
│ - tension = ds_get_haptic_tension(i, &player_spectral_state)
│ = 65536 - <player_HV, ds_e1m1_anchors[i]>·128
│ - cos_sim = 65536 - tension (Q16, [-65536..+65536])
│ - amp = max(cos_sim >> 10, 64) (gain)
│ - sin_val = ds_sin256(phase) (LUT, [-233..+233])
│ - delta = (amp + 32) * sin_val >> 8 (signed Q0)
▼
sectors[i].lightlevel = clamp(baseline[i] + delta, 0, 255)
│
│ DOOM renderer reads sectors[i].lightlevel during R_RenderPlayerView
▼
visible pulse on the wall textures
Where the graph Laplacian shows up: at level load,
G_SpectralResetSecretBaselines walks ds_e1m1_adj to flag every
sector that's graph-adjacent (one hop on the spectral lattice) to a
secret-tagged sector. Those neighbors get a half-amplitude pulse
alongside the secrets themselves. So the secret's hum bleeds
into rooms the player can see, even when the secret itself is
behind a closed door (DOOM's renderer correctly occludes the
secret sector). This is one-step heat-equation diffusion on the
graph -- a truncated form of Track 4, computed on the adjacency
side rather than the Laplacian side. (The full Laplacian-driven
diffusion lives in ds_diffuse_sound for monster awareness.)
Cheat-sequence encoding: classic linuxdoom cheat machinery in
m_cheat.c runs every keypress through a SCRAMBLE bit-permutation
and matches against pre-scrambled byte sequences. idspectral
encodes to:
| i | d | s | p | e | c | t | r | a | l | end |
|---|---|---|---|---|---|---|---|---|---|---|
| 0xb2 | 0x26 | 0xea | 0x2a | 0xa6 | 0xe2 | 0x2e | 0x6a | 0xa2 | 0x36 | 0xff |
Type the letters in the running engine; cht_CheckCheat advances
the per-cheat pointer and on match calls ds_secretglow_set_active.
Toggle pattern is identical to IDDQD / IDKFA — type once to engage,
type again to disengage.
6.6 Verified Live Behaviour (E1M1 / Ubuntu 22.04 / WSLg)¶
Diagnostic output captured live:
DOOM Shareware Startup v1.10
doom-spectral 0.1.0 (linuxdoom-1.10 base)
V_Init / M_LoadDefaults / Z_Init / W_Init -> all clean
adding /usr/share/games/doom/doom1.wad
M_Init / R_Init: InitTextures / InitFlats / InitSprites / InitColormaps
[SPECTRAL] level loaded: 3 secret sectors + 4 graph-neighbor
sectors registered (of 85 total)
P_Init / I_Init / D_CheckNetGame / S_Init / HU_Init / ST_Init
[SPECTRAL] demo version 109 != engine VERSION 110 (jumper engaged;
tic-stream is byte-compatible)
[HAPTIC] sector=38 tension=1.0000 (per tic, 35 Hz)
[SPECTRAL] IDSPECTRAL cheat: ACTIVATED
E1M1 has 1 official secret (the medikit alcove) which the WAD implements as 3 connected secret-tagged sectors (the alcove + two approach steps). The graph-diffusion bleed adds 4 more sectors (the corridor segments adjacent to those 3). Field-tested toggling the cheat shows visible per-tic lightlevel modulation on the non-occluded neighbors, confirming the entire pipeline end-to-end.
7. Replacing FPU Engine Bits with Graph-Laplacian Primitives — A Generic Procedure¶
This section is the chess-spectral Rosetta Stone capstone. The chess notebook (§42) frames a cross-disciplinary methodology for porting any simulation domain to the spectral / ALU substrate. Until now that procedure was abstract. The doom-spectral integration is the first end-to-end demonstration of every step on a real non-trivial codebase, and this section lifts the procedure out of the doom-specific narrative into something reusable.
7.1 The Replacement Pattern¶
For any FPU-leaning subsystem in an existing engine — collision detection, lighting, AI awareness, audio diffusion, inverse kinematics, anything that accumulates spatially-localised effect — the spectral replacement procedure is:
| Step | Action | Doom-spectral example | Chess-spectral analogue |
|---|---|---|---|
| 1 | Enumerate the state space as a finite set of cells. | 85 E1M1 sectors. | 64 chess board squares (§1). |
| 2 | Build the adjacency graph on cells. | ds_e1m1_adj from line crossings + door/lift connections. |
King/knight/etc. piece-type-specific adjacency (§9). |
| 3 | Compute the graph Laplacian \(L = D - A\) and its eigenbasis. | Fiedler vector + spectral partition in ds_e1m1_fiedler. |
Heat-equation diffusion square codebook (§9o.4). |
| 4 | Define per-cell anchor hypervectors in \(\{-1, +1\}^D\) from random φ-vectors. | ds_e1m1_anchors[85][512] (Phase-9 BIP). |
§11 phase-operator move engine vectors. |
| 5 | Encode the agent's continuous state as a hypervector via BIP coprime-shift binding. | ds_bip_encode(x, y, &out) → 512-D HV. |
Piece-position encoder for tactical query. |
| 6 | At each engine tick, query cosine similarity between agent HV and per-cell anchors. | ds_get_haptic_tension(sector_id, &player_hv). |
Tactical resonance score per square. |
| 7 | Diffuse the cosine-similarity field one or more steps along the graph Laplacian. | One-hop ds_e1m1_adj neighbor bleed for IDSPECTRAL; full Euler integration in ds_diffuse_sound. |
Multi-step kernel applied to king-attack square set. |
| 8 | Project the diffused field back to engine-visible state (lightlevel, AI alertness, audio gain, haptic actuator). | sectors[i].lightlevel = baseline + pulse_delta. |
Square-highlight intensity in chess UI. |
The whole procedure is integer-only: every step is a shift,
multiply, modular add, table lookup, or dot-product over int8_t.
No FPU is touched in the hot path. The only floating-point
work in the doom-spectral integration is ds_diffuse_sound's 8-step
Euler integrator (which uses float for clarity but could be
swapped to Q16.16 fixed point trivially); the live IDSPECTRAL
pulse path is pure ALU end-to-end.
7.2 Where the Procedure is Cheaper Than the FPU Original¶
Three places where this typically wins:
- Cosine similarity replaces Euclidean distance. A dot-product
of two 512-D bipolar hypervectors is 512 byte-multiplies plus
one accumulator — gcc auto-vectorises it. The FPU equivalent
(
sqrt((x1-x2)^2 + (y1-y2)^2)on 32-bit floats) costs a multiply-add pair plus ansqrtss. At our typical query rate (35 Hz × 85 sectors = 2,975 queries/s in doom-spectral), the ALU version dominates the FPU version on cache-warm data and wins decisively on cache-cold. - Graph diffusion replaces ray-traced influence. Sound, light,
and AI awareness all want to ask "what's near to here?" The
FPU way: cast rays to candidate neighbors and accumulate. The
spectral way:
field += dt * L * field, one matrix-vector product per tick. For 85 sectors with ~5 neighbors each, the sparse Laplacian-vector product is 425 multiply-adds — way under what a single ray-cast costs. - Anchors decouple geometry from semantics. Once the per-cell anchor hypervectors are fixed, the engine never needs to query geometry again to answer "is the player in a secret?" — it just reads cosine similarity. New gameplay rules slot in by changing the anchor table, not the engine's geometry queries.
7.3 Where the Procedure Has Honest Limits¶
In the spirit of the chess-spectral §42 cross-disciplinary methodological note, three areas where the spectral substitution is not a free lunch and the original FPU subsystem may be preferable:
- Sub-cell precision. The doom-spectral BIP encoder collapses
16.16 fixed-point world coordinates to a 512-bin index via
(uint32_t)x >> 16 % 512. That throws away sub-512-unit resolution. For E1M1's 32k×32k unit map, ~64 world units fit in one BIP bin — about a player diameter. The encoder cannot distinguish two positions inside the same bin. For coarse "what room am I in" queries this is fine; for sub-bin physics (collision response, projectile trajectories), the engine's own 16.16 path stays in charge. - Dynamic geometry. The static lattice tables (
ds_e1m1_*) reflect the WAD at extraction time. Doors, lifts, crushers, destructible walls, and any sector with a moving floor/ceiling have heights that change at runtime. The original Z-fiber gate inp_map.cfailed precisely because it tried to query static data for a dynamic question (§6.1.6). Track 2 still works for static-geometry queries — "could the player ever transition from sector A to sector B given the WAD's static geometry?" — but the answer to "can the player traverse RIGHT NOW" is owned by the engine's live state. - One-shot events. DOOM's secret-credit system zeros
sector->specialafter the player enters once. If your spectral hook gates on the live tag, it stops working after the first visit. Either capture the original tag at level load (which is whatG_SpectralResetSecretBaselinesdoes in §6.5), or accept that the spectral effect is one-shot and design accordingly.
7.4 Capstone Note for the Rosetta Stone¶
The chess-spectral notebook is the Rosetta Stone for "how does spectral graph theory port across game-engine domains?" §1-§9 do the algebra, §11 does the dynamics, §42 does the meta-method, and §20.20–20.21 catalogue the per-domain instances. doom-spectral is the first instance where the full pipeline is wired into a running, playable, originally-FPU game engine — not a research script, not a Python notebook, not a headless simulation, but a 1.5MB ELF binary that opens an X11 window, plays demos, accepts keyboard input, and shows you a graph-Laplacian-driven lighting pulse behind a 1993-vintage cheat code.
That makes the doom-spectral fix branch the end-to-end existence proof for the chess-spectral cross-disciplinary methodology. The other sibling notebooks (othello, antikythera, ephemerides, chess-itself) are partial proofs: each ports one or two layers of the stack to a domain-specific problem. doom-spectral ports the whole stack to a problem where every layer faces the toughest adversary in software engineering — legacy code that's already running and that the spectral substitution must coexist with without breaking. Every other sibling could be rewritten from scratch around the spectral substrate. doom-spectral could not. The fact that the integration succeeded — visible IDSPECTRAL pulse, runnable engine, no broken subsystems — is the strongest available evidence that the procedure in §7.1 generalises beyond greenfield problems.
The remaining tracks (Track 1 spectral BSP partitioning into the renderer; Track 3 sheaf-restriction-map evaluation in the LoS fast-path) are ranked higher in scope but follow the same procedure and the same engine-boundary discipline. They are queued for future ships of the doom-spectral fork.
How to cite this notebook¶
BibTeX:
@misc{kirkland_doom_spectral_2026,
author = {Kirkland, Steven},
title = {DOOM as a Spectral Lattice System --- Research Notebook},
year = 2026,
howpublished = {\url{https://github.com/lemonforest/mlehaptics/blob/main/docs/antikythera-maths/doom_spectral_research_notebook.md}},
note = {Part of \emph{mlehaptics: Spectral-Research Portfolio}; map / level topology + gameplay-system spectral analysis. Project-level citation metadata at \url{https://github.com/lemonforest/mlehaptics/blob/main/CITATION.cff}. Co-authored with Claude Opus 4.7 (Anthropic, 1M-context configuration) per project memory \texttt{feedback\_orchestration\_metaphor}. Framing is one candidate within the project's research portfolio per \texttt{feedback\_no\_lineage\_claims\_in\_notebook}.}
}
Plain text: Kirkland, S. (2026). DOOM as a Spectral Lattice System — Research Notebook. mlehaptics Spectral-Research Portfolio. https://github.com/lemonforest/mlehaptics/blob/main/docs/antikythera-maths/doom_spectral_research_notebook.md
Per-result citation discipline. Specific technical claims cite their canonical sources directly (linuxdoom-1.10 codebase reference, graph-Laplacian textbooks, etc., PDF-verified per [[feedback_pdf_extraction_citation_discipline]]). When citing a specific result, prefer citing both this notebook AND the underlying canonical source. Framings presented here are candidate methodological readings per [[feedback_no_lineage_claims_in_notebook]], not endorsed over alternatives without explicit empirical convergence.
Project-level citation. See CITATION.cff at the repo root for the project-as-a-whole citation form.