The Ludic Trap: When UI Becomes Engine
The story behind The Bottomless Pit
Inspired by Doomscrolling on Wikipedia
Built with Three.js · ShaderMaterial · EffectComposer (ShaderPass) · Canvas 2D
Techniques CRT Barrel Distortion · Bayer Dithering · Scanlines · Chromatic Aberration · Quaternion Interpolation
Direction Make the pun literal — a social media feed that mechanically transforms into a 1993 DOOM corridor using scroll as the only input
Result You doomscroll a feed of darkening posts until GLSL corruption shatters the cards into corridor walls, dropping you into a retro first-person shooter with your social metrics as the HUD
The Story
Doomscrolling is one of those words that arrived already understood. You don’t need it explained. You’ve done it. Everyone has. The compulsive consumption of negative content on an infinite feed, the slot-machine psychology of variable-ratio reinforcement, the ludic loop where the next post is always more interesting than the last. Neuroscience calls it the dorsal vagal drift: your nervous system slowly shutting down while your thumb keeps moving.
The term itself is a pun. Doom as in dread, foreboding, the end of things. But there’s another Doom. The one from 1993. The one with the shotgun and the imps and the E1M1 corridor that changed video games forever.
We wanted to make the pun real.
A previous attempt at this topic (“The Doomscroll”) failed because it treated the concept as a visual metaphor: DOM elements sliding down the page with CSS tweens. Technically trivial. Emotionally flat. The insight this time was that the mechanics of scrolling are the entire point. The scroll bar is the trap. The UI is the ludic loop. The experience shouldn’t illustrate doomscrolling. It should be doomscrolling, and then it should break.
The Take
The concept is a ludonarrative transition: the mechanics of the UI (scrolling a feed) gradually corrupt and evolve into the mechanics of a game (navigating a first-person corridor). You start in a social media feed. You end inside DOOM. The scroll bar is the only input the entire time.
Three phases structure the arc:
The Surface is a clean, familiar social feed rendered entirely on a Three.js canvas. White card backgrounds. Blue and green avatars. Usernames, timestamps, engagement metrics. The posts start wholesome (“Morning light through the studio window”) and progressively darken through anxiety, algorithmic awareness, and existential dread. The content does the narrative work while the visuals stay pristine.
The Glitch introduces GLSL post-processing that corrupts the viewport. A custom fragment shader applies chromatic aberration, Bayer dithering, CRT barrel distortion, scanlines, color quantization from 256 levels down to 6, and horizontal glitch displacement. The feed is still scrolling. The posts are still readable, barely. But the screen is decomposing around them.
The Abyss is the payoff. The feed cards physically fly apart via quaternion interpolation, reforming as corridor walls. The camera falls from its overhead feed position into a first-person view. You’re inside a procedurally generated Doom-style hallway: brick textures, billboarded enemy sprites, fog-based diminished lighting, and a render resolution equivalent to 320x200 upscaled with nearest-neighbor filtering. At the end of the corridor, a boss sprite fills the viewport and a DOOM status bar appears at the bottom of the screen, but instead of ammo and armor, it shows your social media metrics: posts consumed, screen time, scroll depth. You were doomscrolling this whole time.
The Tech
Everything runs on a single Three.js canvas. No DOM-to-WebGL transition, no framework, no libraries beyond Three.js and its post-processing addons. The entire experience is vanilla JavaScript with ES module imports.
Canvas 2D as a texture factory. Every visual asset is generated procedurally at runtime. The social media cards are drawn on offscreen canvases using the Canvas 2D API: rounded rectangles, circular avatars with initial letters, wrapped body text, engagement bars. These canvases become CanvasTexture instances mapped onto PlaneGeometry meshes. The brick wall textures, floor tiles, enemy sprites, boss sprite, and HUD face are all generated the same way. No external images. No asset pipeline. Everything self-contained.
The corruption shader is a single GLSL fragment shader applied as a ShaderPass through Three.js EffectComposer. It takes one uniform, uCorruption (0.0 to 1.0), and derives every effect from it:
- Pixelation: UV coordinates snap to a grid that shrinks from native resolution to 320x200 as corruption increases, using
floor(uv * resolution / pixelFactor) * pixelFactor / resolution - Color quantization: Channel values are bucketed into
mix(256.0, 6.0, corruption)levels - Bayer dithering: A recursive Bayer matrix (
bayer2composed three times for an 8x8 pattern) adds ordered noise proportional to corruption - CRT barrel distortion: UV coordinates are displaced radially from center using
dot(centered, centered) - Chromatic aberration: Red and blue channels sample at offset UVs
- Glitch lines: A hash function flags random horizontal scanlines for displacement
The transition is the trickiest moment. Each card mesh stores two states: its feed position/rotation (vertical stack at z=0) and its wall position/rotation (alternating left/right along the corridor’s Z-axis). A smoothstep over the 65-75% scroll range drives a lerpVectors on position and slerpQuaternions on rotation, morphing the feed into a hallway in real time.
Billboarded sprites handle the Doom enemies. Each sprite is a PlaneGeometry textured with pixel-art drawn via Canvas 2D fillRect calls. In the animation loop, each sprite’s Y-rotation is set to atan2(camera.x - sprite.x, camera.z - sprite.z), keeping them always facing the player.
Fog provides the classic Doom diminished lighting. THREE.Fog is toggled on only during the corridor phase. An earlier bug caused reversed fog (near > far) which rendered everything black during the feed phase, a reminder that Three.js fog with inverted parameters doesn’t disable, it maximizes.
The Experience
You open the page and see a social media feed. It looks normal. @sarah_creates posted about morning light. @tech_pulse is sharing screen time stats. You scroll.
The feed scrolls. @nomad_eats found great ramen. @breaking_alert has urgent climate news. The posts are getting darker but the UI is still clean. You keep scrolling because that’s what feeds are for.
Around the halfway point, something shifts. The edges of the viewport start to shimmer. Colors separate into red and blue fringe. The resolution drops. Horizontal lines of static flicker across the screen. @late_thoughts can’t stop scrolling at 3am. @news_cycle warns about neural damage. The text is hard to read through the interference. You scroll faster.
Then the feed breaks. The cards scatter, flying left and right, rotating 90 degrees, reforming as walls around you. The camera lurches forward. You’re in a corridor. Brick walls. Orange light pooling on the ceiling. Something red floats ahead with one green eye and too many teeth. The scroll bar still works but now it moves you forward through the hallway, past pixelated demons, deeper into a space that runs on 1993 rendering logic.
At the very bottom, a massive horned figure blocks the path. A gray status bar slides up from the bottom of the screen: 1000 posts consumed, 0% health, 4 hours of screen time, 100% scroll depth. A pixelated face stares back at you, bloodied.
You were doomscrolling. Now you’re in Doom.
Experience: The Bottomless Pit
This blog post was AI generated with Claude Code. Authored by Artificial Noodles.