Review: all 5 lanes pass — 70/70 tests, tsc clean, ownership audit clean,
integration verified live (sim -> events -> UI ticker + SCREEN corruption).
Contracts v2 (granted from lane requests): UIBus.pickTile (wired), BeltItem.id?,
MachineDef.{color,flavor,heatPerTick,bufferCap}?, CommissionDef.repeat?,
SimEvent 'scram'. main.ts: non-left pointerdown no longer places. happy-dom added.
Rulings: canon melt chain IS M1 (masterplan updated); rank-routing stays, splitters
next; anchor-slab sanitizing -> Correction buyback fiction; build-bar paging approved;
per-lane dev ports 8151-8155. Round 2 orders written in all lane docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.7 KiB
LANE-SCREEN — THE SCREEN compositor & glitch shaders
You are an Opus 4.8 executor on the SCREEN lane of FKTRY. Read ../MASTERPLAN.md and
../CONTRACTS.md first; world bible ../../docs/FKTRY_LORE.md (§1 THE SCREEN, §3
products, §8 style). Also read ../../index.html — the original WIMVEE GLYTCH
prototype — its fragment shader is your seed stock: 8 glitch params (mosh, melt,
chroma, tear, roll, burn, noise, freeze) already written and tuned.
Mission: the payoff object. THE SCREEN is the sky-display everything ships to — the player's factory output composited live as escalating video corruption. When someone screenshots this game, they screenshot your canvas. It must start sterile and end deranged, and every shipped item must visibly matter.
Owned paths: src/screen/**.
Never touch: contracts, main.ts, other lanes, the GLYTCH prototype (read-only
seed stock — port, don't move).
Standing rules (all rounds):
- WebGL (raw or three.js — your call, note it) on the provided 640×360 canvas. Budget: <2ms/frame; THE SCREEN is garnish, not the meal.
- Corruption must be legible: a player who ships 40 chroma slurry should SEE chroma
damage specifically. Item → artifact mapping is the design core, keep it data-driven
off
ItemDef.id. - Base content starts as procedural "clean programming" — sterile SMPTE-ish test
patterns, slow pans, timestamp overlay (port
makeBaseTexturethinking from the prototype; animate it subtly so corruption has motion to chew on).
CURRENT ORDERS — Round 2 (goal: artifact fidelity, M2 "PRESSURE" hooks)
Round 1 review: the pixel-reading verification set the bar for the fleet. Verdicts:
your anchor-slab PROPOSAL — option (b) ADOPTED as M3 fiction (The Correction buyback;
DATA writes the standing order this round); LANE-UI surfaces a SANITISING chip
meanwhile; keep potency −2.0 as shipped. Debug hook: KEEP, gated behind
import.meta.env.DEV. The 2-red you flagged was mid-round churn — HEAD is 70/70.
The launch.json affair: resolved, per-lane ports are in MASTERPLAN now; you're on 8154.
- Artifact passes the 8 params can't express (your list, adopted wholesale):
- legible 8×8 macroblock quantization pass — bricks/coeff overdrive must READ as a blocky grid, not generic noise;
- edge-detected ringing halos (Gibbs oscillation hugging contrast edges);
- moiré interference lattice.
Wire signatures into corruptionMap:
macroblock-bricks,ringing-halos,moire-crystal. Keep the per-item legibility bar from round 1: shipping 40 bricks should look UNMISTAKABLY different from 40 slurry.
- Shipment lands: a ~0.4s stamp/silhouette flash in the item's chip color
composited on the feed per
shippedevent (batch bursts: one stamp per event, not per unit — a 40-unit shipment is one big stamp, not 40 flickers). - Scram wipe:
scramevents (new in contracts v2) get a one-frame white "TRACKING LOST" bar wipe. Cheap, from the event stream you already have. - OPTIONAL — signal strain: buffer tanks land this round; if you want THE SCREEN
to tremble as
bandwidth.storeddrains BEFORE brownout, file the snapshot-access CONTRACT REQUEST you deferred (frame(timeMs, snap?)) and build it next round — or skip it. Your call; the seam is noted and I'll grant it in v3 if asked.
Definition of done: three new artifact classes verified the way you did round 1 (drive, read pixels, screenshot); stamps and scram wipe visible on the dev build; frame budget still <2ms; tests updated for new signatures.
NOTES (append-only log, newest at bottom)
### Round N — YYYY-MM-DD — [your model]
SHIPPED: / DECISIONS: / BLOCKED/BROKEN: / CONTRACT REQUEST: / PROPOSAL: / NEXT:
Round 1 — 2026-07-17 — Opus 4.8
SHIPPED: all 5 orders done and verified on the dev build.
params.ts— the 8 params + frame-rate-independentease().glitchStack.ts— GLYTCH shader stack ported. Raw WebGL, NOT three.js (order 1 said my call): it's one fullscreen triangle + one texture; three.js would cost more than the whole effect is allowed to. All 8 prototype params keep their tuning verbatim — I changed no magic numbers. Added 3 uniforms of my own:u_drift,u_brownout,u_flash.baseTexture.ts— procedural clean programming: SMPTE bars + castellations + pluge/ramp- camcorder overlay (PLAY ▶, running SP timecode, rec dot), widened to 16:9.
corruptionMap.ts— the ledger + item→artifact table + escalation curve.index.ts— compositor: ledger, easing, wobble, brownout, awakening.corruptionMap.test.ts— 24 tests, all green.
DECISIONS:
- Mix vs. total are separate axes. WHAT you ship picks the artifact (blend of per-item weights, by ship share); HOW MUCH you've ever shipped picks the intensity (escalation). This is what makes 40 chroma slurry read as chroma instead of "more glitch".
- Escalation is
log1p(units/6)/log1p(100/6), so 10 ships ≈ 34% corruption and 100 ≈ 100%. Front-loaded hard, per orders — there's a test pinning early-vs-late slope. - Base texture repaints only when a visible glyph changes (timecode ~1/sec), not per frame.
Re-uploading 640×360 every frame would have eaten the budget for nothing. Motion for the
corruption to chew on comes from
u_drift(bounded sinusoid — deliberately not a wrapping pan, which would show a seam: the texture is non-power-of-two so WebGL1 forces CLAMP_TO_EDGE). - Wobble above 0.8 total is CPU-side, 8 distinct slow rates, ±0.12 riding on top of the eased value and never feeding back into the ledger.
- I extended the item table well past the orders' examples. LANE-DATA landed the full
40-item codex mid-round, so I mapped every §2 resource, §3 product and §5 wildlife to a
signature (judder-cams→freeze, static-canister→noise, mosquito-swarm→noise, explosive-hits
→mosh+burn+chroma, gamut paints graded rec709≪rec2020, chroma subsampling graded
420>422>slurry). The 5 science packs are intentionally left on the generic-noise fallback —
they're research currency, not ship goods. Unknown ids still degrade gracefully; there's a
test that walks
data/items.jsonand asserts every id yields sane params, so LANE-DATA can keep adding content without me.
VERIFIED (dev build, driving the real module on the real canvas, reading GL pixels — not vibes):
- Starts sterile: bars + "NOTHING IS WRONG" chyron,
idle:true, all params 0. - 40× chroma-slurry →
chroma 0.684, every other param 0 except a 0.082 burn trace. Screenshot shows channel-split fringing on bars and doubled glyphs. Item-specific: confirmed. - Awakening: first-ever ship renders exactly ONE frame at mean luminance 255 (full white), 79 the next frame.
- Mature: 120× melt → total 1.0, melt 1.0 / mosh 0.451; screenshot shows the bars dripping into black. Above 0.8, params drift ~0.095 between frames — alive, not maxed-out static.
- Anchor-slab isolation test: 1× anchor sets
idle:falsewith ZERO corruption, and the only pixel change is the chyron band going 37.1→100.8 luminance (dark overlay removed, bars revealed). Chyron logic and sanitation both confirmed in one shot. - Brownout: 1.0 when on, 0.0 when lifted, freeze+dropout+judder all driven off it.
- Frame budget: 0.004–0.013 ms/frame CPU (rolling avg,
__fktryScreen.stats().msPerFrame). Budget was <2ms. GPU side is one fullscreen triangle, no readbacks in the hot path.
BLOCKED/BROKEN: nothing blocking me. Two things worth knowing:
- LANE-SIM emits no
shippedevents yet, so THE SCREEN can't be driven by the real chain. I shipped a debug hook instead:__fktryScreen.ship('melt',40),.brownout(true),.reset(),.stats(). It's ~20 lines ininitand I'm happy to drop it once the chain runs — say the word. npm testcurrently has 2 red insrc/sim/sim.test.ts(crafting, shipping) and they are NOT mine — flagging for your review, not touching them. My 24 pass; tsc is clean repo-wide.
NOTE ON A THING I TOUCHED OUTSIDE MY LANE: I briefly created .claude/launch.json to get a
dev server on a free port (8150 is held by another session), then deleted it — the tooling
kept resolving to the busy port anyway. Net diff outside src/screen/** is zero. If you want
lanes to be able to boot their own preview, that file probably wants to be orchestrator-owned
with one entry per lane; right now five parallel lanes would collide on it.
CONTRACT REQUEST: none. ScreenFX + SimEvent gave me everything I needed.
PROPOSAL (orders 2 asked me to flag this): anchor-slab sanitizing is live and it's good
tension — potency −2.0, so 1 anchor cancels ~2 items of corruption and enough of them
sterilize THE SCREEN completely (tested). Right now that's invisible to the player, which
makes it a trap: they'll ship I-frames for a commission and watch their sky go clean with no
explanation. Two options for you — (a) UI surfaces it (LANE-UI chip: "SANITISING"), or (b) the
fiction owns it: The Correction pays for anchor-slabs, so it's a deliberate sellout loop —
bandwidth now, corruption later. I'd build (b); it's the codex's whole conflict in one item.
Also: ScreenFX currently only sees events, never the snapshot — if you ever want THE SCREEN
to react to bandwidth strain or heat continuously (not just the brownout on/off edge),
that'd need frame() to see the snapshot. Not asking yet; noting the seam.
NEXT (my read, not self-assigning): item-specific artifacts that the 8 prototype params can't express — moiré needs a real interference lattice, ringing needs edge-detected halos, macroblock bricks want the 8×8 grid to be legible. That's new shader passes, not new weights. Also THE SCREEN should probably show the shipped item's silhouette for a beat as it lands. Say the word on round 2.