ENGINEERING LOG // VIEW SOURCE, THEN LOOK CLOSER

BUILD NOTES

Six browser projects opened at the seams: what each experiment was trying to do, how the illusion is assembled, where the design holds, and what should change in the next revision.

▸ OPERATING PRINCIPLES

THE RULES OF THE BENCH

01

READABLE SOURCE

The code should still make sense when opened directly in View Source, without a build pipeline standing between reader and mechanism.

02

STATIC FIRST

Every project must survive on static hosting. State stays local, interaction stays in the browser, and a server is never assumed.

03

ILLUSION OVER ASSETS

Geometry, gradients, oscillators and behavioural rules do more work than downloaded art. Constraints become a visual language.

04

INPUT IS PERMISSION

Microphones and audio contexts start only after a deliberate gesture. If a browser grants a capability, treat it as borrowed.

CASE FILE 01 // BROWSER EFFECTS SHIPPED EXPERIMENT

WEB EFFECTS LAB

A collection of deliberately separable effects: starfield, typewriter, click sparks, microphone waveform, Konami sequence and a proximity-driven synthetic ghost.

Released2026.03.06
RuntimeDOM + Canvas 2D
AudioWeb Audio API
DependenciesNone

Design intent. Make each effect understandable enough to steal. The page is a demonstration surface, but the real product is a set of small mechanisms a visitor can extract and reuse.

User gestureIndependent moduleBrowser primitiveVisible response

HOW IT WORKS

  • The shared script is divided into self-invoking modules, each bound to a small set of element IDs.
  • The starfield keeps a compact array of points and advances them with requestAnimationFrame.
  • The microphone visualiser requests one audio stream, feeds an AnalyserNode with FFT size 512, then draws its time-domain data.
  • The ghost is two oscillators, a low-pass filter, gain stages and a low-frequency oscillator. Distance changes every parameter together.

ENGINEERING NOTES

The convincing part of the ghost is not volume alone. As it approaches, its spectrum opens, both oscillators rise, and tremolo becomes faster and deeper. Several correlated cues imply a physical source moving closer.

Every scheduled parameter ramps over 50 milliseconds. That tiny smoothing interval prevents the clicks produced by instantaneous changes in oscillator or gain values.

LESSONCopyable modules still need boundaries. A shared script must guard missing elements before attaching listeners, and microphone tracks need an explicit stop path.
sound.js // distance becomes timbre
const closeness = clamp(1 - distance / 16, 0, 1);

const gain      = 0.0001 + closeness * 0.16;
const cutoff    = 220 + closeness * 2600;
const freqA     = 110 + closeness * 90;
const freqB     = freqA * 1.5;
const tremRate  = 2.5 + closeness * 6.0;
const tremDepth = closeness * 0.05;

NEXT PASS

  • Guard every module when its expected DOM is absent.
  • Stop microphone tracks when the visualiser is disabled.
  • Suspend animation when the document is hidden.
  • Add an output limiter and a non-audio demonstration mode.
SOURCE PROFILE // VANILLA JS // USER-GATED MEDIA OPEN THE LAB
CASE FILE 02 // CANVAS GAME LIVE BUILD

UNIT_ZERO

A vector arena shooter built from canvas paths, global entity arrays and three enemy behaviours that create readable pressure without an engine.

Released2026.03.07
RuntimeCanvas 2D
SimulationFrame loop
DependenciesNone

Design intent. Prove that enemy character comes from motion and timing before it comes from artwork. Every hostile is a few lines, rectangles and circles; its behaviour supplies the silhouette the geometry cannot.

Read inputUpdate entitiesResolve collisionsRender frame

HOW IT WORKS

  • The player, bullets, hostile bullets, enemies, particles and glitch lines live in separate arrays.
  • Circle-distance tests handle all impacts. Dead entities are removed with filtering after collision resolution.
  • Scouts ram quickly; Walkers close to firing distance and hold; Tanks stop farther out, strafe and track with an independent turret.
  • Wave size follows 3 + wave × 2. The spawn pool introduces Walkers and Tanks gradually rather than merely increasing health.

ENGINEERING NOTES

The game favours immediate data structures over formal classes. That keeps the source short and readable: an enemy is an object literal, while its type selects drawing and steering behaviour.

Hit flashes, radial particles and temporary glitch lines carry impact feedback. They are cheap enough to generate procedurally and consistent with the vector aesthetic.

LESSONThree genuinely different movement policies produce more tactical variety than a long list of enemies sharing one pursuit algorithm.
unit_zero.js // escalation policy
const count = 3 + wave * 2;
const pool = wave < 2
  ? ["SCOUT"]
  : wave < 4
    ? ["SCOUT", "SCOUT", "WALKER"]
    : ["SCOUT", "WALKER", "WALKER", "TANK"];

NEXT PASS

  • Use elapsed time so speed is independent of refresh rate.
  • Pool bullets and particles to reduce allocation spikes.
  • Add a spatial grid before increasing entity counts.
  • Provide touch controls and pause automatically on blur.
SOURCE PROFILE // VECTOR GEOMETRY // ARRAY-BASED ENTITIES START UNIT_ZERO
CASE FILE 03 // PROCEDURAL 3D PROCEDURAL BUILD

MAZE RUNNER v3.5

A generated first-person maze that guarantees a route, places its exit by graph distance, keeps its pursuer away from the opening move and turns proximity into sound.

Released2026.03.12
RuntimeThree.js / WebGL
Grid31 × 21 cells
AudioProcedural Web Audio

Design intent. Random must never mean arbitrary. The layout can surprise the player, but the generator is responsible for solvability, meaningful separation between start and exit, and a fair opening interval before the ghost becomes dangerous.

Recursive carveBFS distancesFarthest exitScored ghost spawn

HOW IT WORKS

  • A recursive backtracker moves two grid cells at a time, cutting the wall between them. The result is one connected perfect maze.
  • Breadth-first search measures real corridor distance from the start. The farthest reachable open cell becomes the exit.
  • The ghost candidate score combines distance from the exit with distance from the player and rejects positions too close to the start.
  • Player collision samples nine points around a radius and resolves X and Z independently, allowing movement to slide along walls.

ENGINEERING NOTES

The textures are also procedural: small hidden canvases generate brick and floor patterns, which become CanvasTexture objects. The maze therefore needs no image assets.

The minimap is a second rendering of the same grid state, not a screenshot of the 3D world. This keeps it cheap and makes game logic the single source of truth.

LESSONA generator should output proof as well as content. The BFS distance map proves reachability and becomes useful again for exit and enemy placement.
maze_runner35.html // generation contract
maze = generateMaze(31, 21);

const start = { x: 1, z: 1 };
const distance = bfsDistances(maze, start.x, start.z);
const exit = pickFarthestOpenCell(distance, maze);
const ghost = pickGhostSpawn(maze, exit, start);

NEXT PASS

  • Replace queue shifting in BFS with an indexed queue.
  • Seed the generator so interesting mazes can be shared.
  • Instance repeated wall geometry to reduce draw calls.
  • Add an explicit navigation graph for smarter ghost pursuit.
SOURCE PROFILE // GRAPH ALGORITHMS // GENERATED TEXTURES ENTER THE MAZE
CASE FILE 04 // BEHAVIOURAL SIMULATION LIVE ECOSYSTEM

AQUARIUM TERMINAL

A canvas aquarium where weighted local rules generate schooling, hunger redirects attention, predators create panic and a retro wrapper turns the simulation into a found appliance.

Released2026.03.28
RuntimeCanvas 2D
Population6–60 community fish
DependenciesNone

Design intent. The tank should feel inhabited without pretending to be biologically exact. Behaviour is layered: schooling establishes normality; appetite, predators and the pointer disturb it; damping lets the school find a new equilibrium.

Sense neighboursAccumulate forcesLimit velocityDraw living state

HOW IT WORKS

  • Each fish examines neighbours inside 120 pixels, accumulating separation, velocity alignment and movement toward the group centre.
  • Hunger increases over time and expands the radius within which food is attractive. Eating removes a pellet and reduces hunger.
  • Predators and the mouse exert repulsive forces. Panic raises the fish's permitted speed, visibly scattering the school.
  • The frame delta is capped at 33 milliseconds, limiting unstable jumps after a stalled or backgrounded frame.

ENGINEERING NOTES

The neighbour scan is quadratic, but the interface limits the main school to 60 fish. At that scale, the direct implementation is easier to read and remains fast enough.

Light cycles, rays, plants, bubbles, rocks and shells are all procedural. The simulator and its late-1990s portal wrapper are separate files, keeping presentation independent from behaviour.

LESSONEmergence becomes legible when every force has a different visual consequence. Schooling, feeding and panic should not merely change numbers; they should change the shape of the group.
advanced_fish_tank_simulator.html // weighted schooling
// alignment
ax += (averageVX - fish.vx) * 0.24;
ay += (averageVY - fish.vy) * 0.24;

// cohesion
ax += (centreX - fish.x) * 0.08;
ay += (centreY - fish.y) * 0.08;

// separation is applied at close range before both

NEXT PASS

  • Add spatial buckets before allowing larger populations.
  • Seed species, decor and starting positions for repeatability.
  • Pool food and bubbles instead of repeatedly splicing arrays.
  • Persist operator settings in local storage.
SOURCE PROFILE // BOIDS-INSPIRED RULES // DELTA-TIME LOOP OPEN THE TANK
CASE FILE 05 // INTERFACE FICTION DESKTOP ONLINE

AI 2K

A Windows 2000 shell wrapped around a stubborn local assistant. The illusion comes from faithful interface grammar, delayed responses and a close button that refuses its job.

Released2026.03.11
RuntimeDOM + CSS
Response engineLocal keyword rules
DependenciesNone

Design intent. Recreate the emotional logic of an old desktop rather than a pixel-perfect operating system. Familiar controls establish trust; the assistant violates that trust in small comic ways.

User textCommand or keywordTimed responseCharacter state

HOW IT WORKS

  • The outer page owns the desktop, icons, taskbar and clock. The assistant runs in a dedicated iframe with matching background.
  • A small rule table maps keyword groups to response lists. Unmatched text selects a fallback line.
  • Slash commands provide deterministic actions: clear conversation, show help and trigger a dance animation.
  • The close control scales the window away, waits 1.5 seconds, restores it and comments on the failed attempt.

ENGINEERING NOTES

The page calls itself AI, but no model or network service is involved. The response delay and animated thinking state are theatrical timing devices; the actual engine is transparent and deterministic apart from response selection.

User input enters the transcript through textContent, while authored assistant messages use innerHTML to support command formatting. That separation prevents typed markup from becoming page markup.

LESSONInterface fiction works when behaviour contradicts affordance. A close button that visibly closes and then reverses itself tells a stronger story than a paragraph about an intrusive assistant.
clippy2k.html // deliberately small brain
for (const category in brain) {
  if (category === "fallback") continue;
  const hit = brain[category].keywords
    .some(keyword => input.includes(keyword));
  if (hit) return choose(brain[category].responses);
}
return choose(brain.fallback);

NEXT PASS

  • Make windows draggable and model focus order explicitly.
  • Persist the transcript without sending it off-device.
  • Add keyboard equivalents for every desktop control.
  • Move authored rich messages through a narrow template API.
SOURCE PROFILE // RULE ENGINE // INTERFACE AS NARRATIVE BOOT AI 2K
CASE FILE 06 // CINEMATIC WEBGL WEATHER UNSTABLE

FORBIDDEN CUBE

A cinematic Three.js anomaly assembled from procedural texture, fog, instanced buildings, layered particles, scheduled events and sound that waits to be invited in.

Released2026.03.11
RuntimeThree.js 0.160.1
Particles2,600 rain / 950 ash
Audio assetsWind / thunder / UFO / radio

Design intent. Make a single object feel embedded in a world larger than the viewport. The cube barely needs to act; weather, distant infrastructure and rare aerial events imply that somebody else has been watching it for years.

Procedural sceneContinuous weatherScheduled anomaliesUser-gated sound

HOW IT WORKS

  • An import map loads Three.js modules and OrbitControls. A wrapper page isolates the cinematic viewport from surrounding fiction.
  • Fifty-four skyline buildings share one geometry and material through InstancedMesh.
  • The crate face is drawn into a hidden canvas and promoted to a texture; no cube image is downloaded.
  • Rain and ash are typed position arrays. Their update functions mutate buffer data rather than replacing scene objects.
  • Lightning, green pulses, glitches and UFOs use separate future timestamps, keeping rare events irregular.

ENGINEERING NOTES

The scene combines three depth cues: exponential fog hides the edge of the world, a layered skyline supplies scale, and rain sheets supplement individual rain particles near the camera.

Audio is explicitly enabled by button. Once granted, ambient wind loops while thunder, radio and UFO sounds are allocated as one-shot events.

LESSONCinematic density does not require unique objects. Repetition becomes atmosphere when variation comes from transform, timing, opacity and depth.
storm-cube.html // one draw family, many buildings
const buildings = new THREE.InstancedMesh(
  sharedBoxGeometry,
  sharedBuildingMaterial,
  54
);

dummy.position.set(x, height / 2, z);
dummy.scale.set(width, height, depth);
dummy.updateMatrix();
buildings.setMatrixAt(index, dummy.matrix);

NEXT PASS

  • Host the Three.js modules locally for archival resilience.
  • Add low, medium and cinematic quality profiles.
  • Pause weather and event timers on hidden tabs.
  • Provide audio failure states and captions for radio events.
SOURCE PROFILE // INSTANCING // BUFFER PARTICLES // EVENT SCHEDULER OBSERVE THE CUBE

▸ CROSS-PROJECT REPORT

WHAT THE BUILDS REPEAT

THIN SHELL, FOCUSED CORE

Several projects pair a themed wrapper with a separate runtime: Unit Zero, the Aquarium, AI 2K and the Cube. The pattern works because the fiction can change without disturbing the simulation.

PATTERN // PRESENTATION ≠ ENGINE

STATE IN PLAIN ARRAYS

Bullets, fish, food, bubbles, rain and ash all use direct arrays. At current scales this keeps code inspectable. Beyond those scales, spatial indexing and pools become the next architectural step.

THRESHOLD // SIMPLE UNTIL MEASURED

PROCEDURAL COHERENCE

Generated texture, geometry, movement and sound share the same visual constraints. The result feels authored because every subsystem speaks the same limited vocabulary.

RULE // FEWER ASSETS, STRONGER LANGUAGE

TIME NEEDS A CONTRACT

The Aquarium and 3D scenes use elapsed time; Unit Zero advances mostly per frame. Standardising on capped delta time would make behaviour consistent across 60, 90 and 144 Hz displays.

DEBT // FRAME-RATE INDEPENDENCE

GUARD THE PAGE BOUNDARY

Reusable scripts should assume their target elements may not exist. A component that exits quietly when absent is safer to share across a growing static site.

DEBT // NULL GUARDS + CLEANUP

THE WEB IS THE ENGINE

Canvas, WebGL, Web Audio, CSS animation and ordinary DOM events cover the entire catalogue. The browser is not merely the delivery layer; it is the creative toolchain.

STATUS // NO BACKEND REQUIRED