▲ ◆ ▼ ✦ ◇ ◆ ▼ ▲ ✦ ◆ ▼ ▲ ◆ ✦

INFINI RACER

⚡ ALL VERSIONS ⚡ A LIVING SCROLL OF EVERY ITERATION ⚡

◀ BACK TO HOME
V024
★ track-intro: responsive + snappier ★
▶ PLAY V024

PROMPT

oops the track title doesn't fit on a mobile screen, adjust accordingly and fade it out quicker

CHANGES

  • Responsive title sizing. Measures the track name at the ideal 84 px and shrinks to fit W · 0.88. Long titles fit cleanly on narrow phones; desktops get the full size
  • Snappier intro. TRACK_INTRO_DURATION 3.6 s → 2.4 s; TRACK_INTRO_HOLD 1.7 s → 1.3 s
V023
★ track-intro: pop + fade-out-while-growing ★
▶ PLAY V023

PROMPT

actually it's fine, but show the track name more prominently at the start of the race then move into into position at the top in a cool way (follow-up) always show the track name at the top and fade it out instead of moving it to the top. it should fade out while increasing in size

CHANGES

  • Big centred track intro at the start of every track. Two-line readout — TRACK NN in amber + the track name in white-on-cyan-halo at ~84 px Press Start 2P
  • Pop-in (0 → 0.35 s): overshoot bounce scale 0.5 → 1.15 → 1.0 + a brief white screen-flash
  • Hold (0.35 → 1.7 s): full size centred at H · 0.42
  • Fade-out + grow (1.7 → 3.6 s): scale 1.0× → 2.0× while alpha 1 → 0 — title expands past the camera
  • HUD #track-label stays visible the whole time; canvas intro plays on top
  • Race clock paused while introT < TRACK_INTRO_DURATION
V022
★ multi-track campaign + named tracks ★
▶ PLAY V022

PROMPT

ok so this is track 1. maintain the random seed for track 1 but choose other seeds for the other tracks, after track 1 comes track 2 and so on, give each track a new name based on boards of canada song titles, these should be random but seeded so procedural. inform the player of which track they're about to play. also if you finish a track, leave the score on a bit longer before going to the next track. after crossing the line the vehicle should slow down so as not to crash

CHANGES

  • Track 1 keeps the legacy seed (7341). trackSeedFor(n) returns 7341 for track 1; tracks 2+ derive a deterministic seed from hash01(n · 13.37 + 17.6) · 1 000 000
  • Track names. A TRACK_NAMES array of band-song-title identifiers used as level labels. Track 1 is hard-coded to the first entry (ROYGBIV); trackNameFor(n) picks the rest via hash01(n · 11.7 + 3.3)
  • Multi-track campaign. race.trackNum / trackName; runScoreAccum carries score across tracks; advanceToNextTrack() regenerates the world with the new seed but preserves score. commitReset() is now the failure path only
  • Persistent TRACK label at the top of the HUD with diff-caching
  • Finish overlay extended 6 s → 9.5 s with a two-phase crossfade: score breakdown then NEXT TRACK callout
  • Vehicle slows down past the line. throttle=0, brake=1 override during the post-finish hold; off-track crash check skipped so a curve after the flag can't wipe out the celebration
  • Crash readout (WIPEOUT / TIME OVER) shows the full accumulated score
V021
★ tighter clock + longer race ★
▶ PLAY V021

PROMPT

ok tweak it so it's only 60 seconds per stage, and make them slightly further apart

CHANGES

  • Per-stage duration 90 s → 60 s. Total base budget 270 s → 180 s
  • Distances 60 / 140 / 240 k → 65 / 150 / 255 k. Gaps now 65 k / 85 k / 105 k
  • Net: time-out back in play even with carry-over. Need ~1500 u/s (~94% MAX_SPEED) to finish on the wire
V020
★ longer stages + carry-over + CHECKPOINT! ★
▶ PLAY V020

PROMPT

ok a few things, make the stages further apart say 10 times further apart, add any remaining time at checkpoint to the new countdown. give the player 90 seconds per stage and when you pass a checkpoint the word checkpoint! should be displayed in a cool way and then disappear.

CHANGES

  • Stages 10× further apart. Distances 6000 / 14000 / 24000 → 60 000 / 140 000 / 240 000
  • 90 seconds per stage across the board (was 22 / 22 / 25)
  • Time carries over at checkpoints. Leftover seconds added to the next stage's 90 s — fast play compounds
  • CHECKPOINT! banner renders centre-screen on every intermediate cross. Snap-zoom from 1.4× scale to 1.0× while fading in (0.15 s), holds ~0.9 s, then fades out over 0.45 s. Two-pass text — wide amber drop-shadow halo + slightly smaller white-on-amber core
  • Final flag still skips the CHECKPOINT! banner — the finish overlay takes over
V019
★ OutRun-style timed stages ★
▶ PLAY V019

PROMPT

ok let's make a radical change. it's too easy to get a high score by simply driving slowly. add an outrun style countdown where you must complete 3 stages in a certain time, think of it as 3 checkpoints, when you hit a checkpoint before time runs out the countdown resets after the third stage you get to the chequered flag and any time you had left on the clock is generously factored into your score. maintain the style.

CHANGES

  • 3-stage race with countdown clock. Stage distances: 6000 / 14000 / 24000 units. Per-stage durations: 22 / 22 / 25 seconds. Each stage's checkpoint resets the timer to the next stage's duration. Reach the chequered flag at 24000 to finish
  • TIME OVER — when race.timeLeft hits 0, triggerCrash("timeout") fires the existing slow-mo explosion sequence with a "TIME OVER" label instead of "WIPEOUT" (separate crashReason flag distinguishes them)
  • Generous bonus at the flagrace.finalBonus = floor(timeLeft × 250,000) added on top of the base score (speed × distance at flag-cross)
  • Top HUD restructured: big Press Start 2P #timer at top centre with urgency colours — cyan above 10 s, amber 5–10 s, rose under 5 s with a step-blink animation. Below it a pip indicator 1 ✦ 2 ✦ 3 with the active stage glowing amber and completed stages in cyan. Score sits below the pips at a smaller size
  • Intermediate checkpoints draw as a bright amber bar across the track; final is a two-row alternating white + cyan chequered banner
  • Finish screendrawFinishOverlay() renders "FINISH!" plus a score breakdown over a fading backdrop, holds 6 s then auto-resets
V018
★ start-screen pickup tutorial ★
▶ PLAY V018

PROMPT

explain what the hexagon pick ups do at the start, maybe show an example of the spinning hexagon pickup

CHANGES

  • New SHIELD PICKUP tutorial card in the start overlay — a live SVG hexagon that spins (5s linear infinite) with a pulsing white core (0.9s ease-in-out), matching the in-game magenta colour + glow
  • One-line explanation next to the demo: "grab one to charge the side rails for 5 seconds — the track edges push you back harder, so you can lean into corners"
  • Styled to match the overlay aesthetic: dashed magenta border, low-opacity magenta tint, Press Start 2P label + VT323 description
  • SVG uses drop-shadow filters for the phosphor glow so the demo reads at the same vector-tube intensity as the in-game pickup
  • Fix pass after first cut: the hex was orbiting (translating in a wide circle) rather than spinning in place. Cause: transform-origin: center on an inner <g> resolves to the SVG viewport's top-left in some browsers because transform-box defaults are inconsistent. Moved the spin to the outer <svg> element (a DOM element where transform-origin: center is well-behaved), kept the core's pulse on the inner circle with transform-box: fill-box so its scale stays pinned to its own centre. Added 6 white vertex phosphor dots that ride along with the rotation — matches the in-game render where each beam() call auto-emits dots at both endpoints
V017
★ brake-to-drift around corners ★
▶ PLAY V017

PROMPT

ok lets try adding drift around corners when the brake is applied

CHANGES

  • New car.slipAngle field — radians offset between the car's heading and its actual motion direction. Translate now uses motionDir = heading + slipAngle instead of plain heading, so the world scrolls diagonally during a slide
  • Drift trigger: brake held + steer commitment over DRIFT_STEER_MIN = 0.18 + speed over DRIFT_MIN_SPEED = 220. Below the threshold the brake is just a brake (no slide)
  • Yaw boost while drifting (DRIFT_YAW_BOOST = 1.55) — heading rotates faster than motion. Slip angle eases toward ±DRIFT_MAX_SLIP = 0.50 rad (~29°) opposite the steer at 1/τ = 5.5. When the trigger releases, slip decays back to zero at 1/τ = 6.5 — grip recovers
  • Speed dynamics: brake during drift is softened (DRIFT_BRAKE_MUL = 0.30) so the slide actually continues, plus DRIFT_DRAG = 90 u/s² of bleed so it isn't free speed
  • Visual cue: wheels brighten to hot orange (#ff8d3a) and thicken (1.4 → 1.8 px) while |slipAngle| > 0.06 rad. The diagonal world-scroll already shows the slide; the brighter wheels just emphasise it
  • commitReset() clears slipAngle alongside the rest of the car state
V016
★ shield power-ups (rolled back from ramps) ★
▶ PLAY V016

PROMPT

ok I don't like the ramps, keep this version for reference but rollback and add power ups on the course that make the repellent effect of the side of the course stronger, during this period have the vehicle flash

CHANGES

  • Rollback: ramps + airborne physics dropped from latest/; v015 stays in the snapshot folder as the "we tried ramps" reference. Codebase is back to the v014 baseline before layering the new feature
  • Power-ups: sparse pickups every 1500–2600 units (deterministic from worldSeed). Visual: a magenta wireframe hexagon rotating slowly at phase × 1.2, with a pulsing white phosphor core dot — clearly distinct from track (cyan) and scenery (amber)
  • Pickup detection: when the car centre is within POWERUP_RADIUS = 24 of an unpicked one (along-track + lateral together), the pickup is marked taken and car.boostT = 5 seconds
  • Boost effect: while car.boostT > 0, edge repulsion is multiplied by 3.0× (max push 620 → 1860 u/s) — well above MAX_SPEED, so the edge becomes a near-soft-wall you can't easily punch through. Returns to baseline when the timer expires
  • Vehicle flash: while boost is active, body / cockpit / wheel colours switch to a magenta-tinted palette at ~6 Hz via (Math.floor(phase × 12) & 1). Square wave gives a sharp on/off blink that reads as "powered up" rather than a soft glow
  • commitReset() clears the powerups array + boost timer; trimWorld() drops pickups (taken or not) once they're well behind
  • Fix pass: the "power-ups disappear" bug — four call sites computed track indices as Math.floor(s / SEG_LEN), but after trimWorld() splices old segments off the front, track[0].s is no longer 0 so that math points at the wrong row (or clamps to the last segment, making the hexagon snap to the end of the visible track). Added a trackIndexAt(s) helper that uses track[0].s as the base offset; rewired scenery placement, power-up gen, power-up render, and distance markers through it
  • Hexagon visual radius 14 → 21, pickup hit radius 18 → 24
V015
★ ramps + airborne ★
▶ PLAY V015

PROMPT

ok let's try adding occasional ramps to the course, when you hit a ramp the vehicle leaves the ground and the road fades a bit and allows the vehicle to fly over any part of the course but must land on the track again or wipeout

CHANGES

  • Ramps: sparse procedural launches placed every ~900–1700 units (deterministic from worldSeed). Each ramp is a wireframe wedge — a forward-pointing triangle with two perspective cross-bars narrowing toward the apex — drawn in amber so it pops against the cyan track edges. Width clamped to a fraction of the (variable) track half-width so it never blocks the track
  • Airborne physics: hitting a ramp footprint (along-track and lateral) launches the car. altVel = 220 + (speed/MAX_SPEED) · 460, gravity 720 u/s². While airborne: forward velocity preserved (no throttle/brake/coast), edge repulsion off, off-track crash check skipped. Steering still works at ~45% authority. On landing, if the vehicle lands inside the track edges → safe; if not → triggerCrash()
  • Visual fade: worldAlpha = 1 − 0.6 · min(1, altitude/70) applied to only the G_TRACK + G_SCEN beams and dots — track + scenery dim while the car + thrust + intersection sparkles stay full strength. Group tag now travels with each dot too
  • Car scale-up: while airborne, render scale = zoom · (1 + altitude · 0.0065) — the vehicle grows toward the camera as it climbs, gives the lift cue without touching the camera transform
  • commitReset() clears airborne state + ramps array; trimWorld() drops ramps far behind the player alongside scenery
  • Tuning pass: jumps higher + longer airtime, ramps sparser, fade stronger:
    • GRAVITY 720 → 600 / LAUNCH_BASE 220 → 360 / LAUNCH_BONUS 460 → 680 — full-speed jumps now ~3.5 s airtime peaking ~900 u (was ~1.9 s / ~320 u)
    • RAMP_GAP_MIN 900 → 2000, RAMP_GAP_RANGE 800 → 1600 — ramps now every 2000–3600 units (≈ half as many)
    • World fade: worldAlpha = 1 − 0.78 · altitudeFactor (was 0.60), saturating at altitude 90 (was 70). Track + scenery drop to ~0.22 alpha at peak (was 0.40)
    • Car-scale formula clamped via Math.min(2.4, 1 + altitude · 0.004) so very high jumps don't make the car fill the screen
V014
★ start screen tidy ★
▶ PLAY V014

PROMPT

the key info on the first screen is a little messy, tidy that up a bit

CHANGES

  • Controls list rebuilt as a 4-row grid: keys right-aligned | action left-aligned, with a fixed column gap. Each row covers one direction so the layout is symmetrical (LEFT and RIGHT are now their own rows instead of being lumped together as STEER)
  • kbd elements get a fixed min-width: 28px + display: inline-flex so arrow-glyph and letter keys all render the same size — no more uneven gaps from glyphs of different widths
  • Arrow + letter alternates sit beside each other (no / separator); the grouping reads naturally without punctuation noise
  • Action labels coloured cyan with a soft glow so they pop next to the white keycaps; old reliance on &nbsp; for spacing removed
  • "OR DRAG THE JOYSTICK" → "OR USE THE JOYSTICK" (covers both touch and mouse)
V013
★ joystick lifted off bottom edge ★
▶ PLAY V013

PROMPT

ok - small change move the joystick up a bit because on mobile it's too close to the edge of the screen causing unintentional gestures

CHANGES

  • Joystick centre raised: cy = H - r - 80 (was H - H · 0.10) — keeps a consistent ~50 px clearance below the BRAKE / STEER labels and out of the iOS / Android swipe-up gesture zone, regardless of viewport height
  • Pointer hit-zone updated to follow the joystick (py ≥ stick.cy - stick.r - 20) so the top of the knob stays grabbable now that the stick sits higher
V012
★ rollback to canvas-2D ★
▶ PLAY V012

PROMPT

do 2 (option 2 from the v011 follow-up: rollback)

CHANGES

  • WebGPU code path removed from latest/ — back to single-canvas, canvas-2D-only rendering
  • Stripped: WGSL shaders, initWebGPU, two-canvas stack, ?renderer=webgpu flag, render-loop branches, resize hooks for the GPU canvas
  • v011 stays frozen as the "we tried this" reference. The shader work + integration glue is preserved there if we want to revisit when scene density actually outgrows canvas-2D
  • Net code delta: game.js ~1500 lines → ~1200 lines; styles.css back to single canvas rule; index.html back to single canvas element

WHY

  • At v010 scene density on a fast Mac, both renderers easily hit the 60 fps cap, so the WebGPU win is invisible — it's headroom for content we don't have yet
  • The shader-rendered output looked slightly less crisp than the canvas-2D layered strokes. A second shader pass tightened it but at that point we're maintaining two render paths for parity, not headroom
  • Decision: pay the perf cost when there's content that needs it; until then, the simpler single-path canvas-2D code wins on maintainability
V011
★ webgpu opt-in ★
▶ PLAY V011 (WebGPU)

PROMPT

I still feel like if we use webgpu we'd free ourselves up to worry less about performance, we can always rollback if it doesn't work

CHANGES

  • Optional WebGPU renderer for beams + dots. Activated by ?renderer=webgpu in the URL; falls back automatically to canvas 2D if navigator.gpu is missing or device init fails. Game logic, joystick, crash explosion, and HUD stay on the canvas-2D layer
  • Two-canvas stack: <canvas id="game-gpu"> at z-index 0 (WebGPU output, opaque, full screen) sits behind the existing <canvas id="game"> at z-index 1 (canvas-2D, cleared transparent when GPU is active so the layer below shows through)
  • Line shader (WGSL): one instance per beam, vertex shader expands a quad along the line with rounded-cap glow padding, fragment shader computes capsule SDF and outputs color × (crisp + exp-falloff glow) with additive blend. Single draw call replaces the 2-pass canvas-2D stroke loop
  • Dot shader (WGSL): billboarded quad per dot, fragment evaluates halo + mid + hot-core layers from one radial SDF in a single draw call. Replaces the 3-fill-per-dot canvas-2D loop
  • Hex-color → RGBA float cache so per-frame instance buffer marshalling is one Map lookup per beam/dot
  • Per-frame instance buffers grow on demand (start 256 lines / 512 dots, double when needed) and are reused across frames
  • device.lost handler clears the webgpu reference so we automatically fall back to canvas 2D if the GPU disappears mid-session
  • Canvas-2D crisp beam pass deduplicated (an earlier edit had accidentally left a duplicate copy)

OPENING NOTES

  • To opt in: append ?renderer=webgpu to the URL (the PLAY button on this card already does)
  • Without the flag, the game runs the same canvas-2D path as v010
  • If your browser doesn't support WebGPU (Firefox < 141, Safari < 26, Chromium without GPU), it logs a warning and silently falls back

SHADER CRISPNESS FIX

  • First pass used exponential glow falloff (exp(-d * k)) which bled softer than canvas-2D's hard-edged 2-pass stroke. Switched both shaders to smoothstep with hard edges matching the canvas-2D layers exactly: line crisp inner (α 1) + mid-glow ring (α 0.26) at width × 2.6; dot halo + mid + white core as three hard-edged disks. Smoothstep starts at the disk/line edge so the interior keeps full intensity (no AA softening of thin lines)
  • Performance honesty note: at 60 fps cap on a fast Mac, both renderers easily hit vsync — the WebGPU win is headroom, only visible if scene density grows past what canvas-2D can sustain. If that headroom isn't currently valuable, this version can be ignored and v010 remains the active baseline
V010
★ perf + leak fix ★
▶ PLAY V010

PROMPT

there seems to be a few performance issues, especially in Firefox, please look into improving performance, maybe we can use webgpu related stuff. also with firefox I left ot on overnight and it slowed to a crawl. memory leak?

CHANGES

  • Memory leak fix: trimWorld() splices old track[] segments + deletes far-behind sceneryChunks entries every ~1s; lastSegIdx is adjusted so the window math stays valid
  • Intersection scan capped at 6000 pair checks per frame so heavy scenery can't make it dominate
  • Vignette gradient cached and rebuilt only on resize (was being recreated every frame)
  • visibilitychange handler gates the entire frame body when the tab is hidden and resets last on return
  • FPS readout added to the HUD for quick A/B perception checks without DevTools
  • WebGPU not used — bottlenecks were JS-side state churn + unbounded arrays; canvas 2D handles this scene fine if we don't fight the rasterizer
  • Hotfix: moved _vignette declaration to the top of the IIFE — initial resize() ran before let _vignette, throwing TDZ ReferenceError on load
  • Reverted: the path-batched beam + dot renderers I shipped first turned out to be slower in both browsers (worse in Firefox). Building one big path with hundreds of arc/line subpaths and filling it under "lighter" hits the complex-path slow path; the state-change savings don't pay back. Back to per-element draws for now
  • The actual win — Firefox profile pinpointed it: drawScanlines was eating 67% of frame time (full-screen fillRect with globalCompositeOperation = "overlay"), and the per-frame radial-gradient vignette another 14%. 81% of CPU on two effects that don't need to be in JS at all. Both moved to body::before / body::after CSS pseudo-elements layered over the canvas — the browser compositor handles them effectively for free. Visual result identical
  • Follow-up profile (21× fewer total samples) revealed two smaller spikes — both addressed:
    • Joystick labels: re-issuing ctx.font + 3× fillText every frame triggered Style computation. Pre-baked to three offscreen canvases at updateStickGeom; render now does drawImage
    • updateHUD: was setting textContent on 4 spans + running toLocaleString + writing to localStorage every frame the score climbed. Now diffed against last-displayed values — DOM only updates on actual change. Best-score writes debounced via setInterval(saveBestScore, 3000) + pagehide save
    • carTrackInfo() result cached per frame so render() and updateHUD() share one nearest-segment search
  • Follow-ups (third profile pass):
    • Beam glow reduced to 2 passes. Dropped the wide × 5 / α 0.10 outer-glow pass — widest line caps to rasterize, contributed least to bloom. Mid pass slightly bumped (× 2.4 α 0.22 → × 2.6 α 0.26) so the visual stays close. Kept
    • Joystick labels bake made dpr-aware (was rendering at 1× and being upscaled on retina displays). Kept
    • Tried + reverted: baked the joystick's static rings / ticks / glows into two offscreen canvases (one source-over, one composited "lighter") so each frame would be 2 drawImage calls. Profile showed it was slower in Firefox — drawJoystick went 5.2% → 8.4%, and drawImage appeared as a brand-new 7.9% entry. Likely cause: the bake bbox is mostly transparent space but Firefox's "lighter"-blend canvas-to-canvas drawImage still pays per-pixel work over the full ~350×350 region, while the original live draws only paid for actually-stroked pixels. Reverted to the previous live drawJoystick. Cardinal ticks now use a single 4-subpath stroked path (one stroke call instead of four) — small consolation
V009
★ different seed ★
▶ PLAY V009

PROMPT

I don't like this track. change the seed

CHANGES

  • worldSeed 1 → 7341 in both the initial assignment and commitReset()
V008
★ deterministic world ★
▶ PLAY V008

PROMPT

just do the first thing

CHANGES

  • World seed pinned to 1 instead of being randomized on load + on reset — same track every run
  • Same fixed seed used after a crash so the world is identical post-reset
V007
★ anti-magnetic edges ★
▶ PLAY V007

PROMPT

ok the edges of the track are slightly repellent, almost antimagnetic but if you go too fast this won't be enought to stop you going out, the closer to the edges the more you feel it though

CHANGES

  • Quadratic edge-repulsion velocity field — proximity²·EDGE_REPEL_MAX (620 u/s) added laterally toward the centerline
  • Repel zone min(75, hw·0.6) so narrow tracks shrink the field with them
  • Slow drift is corrected; fast perpendicular runs (lateral component up to 1600 u/s) punch through
  • Visualisation: nearest edge thickens with proximity + pulsing inward "field ticks" on every nearby segment
  • car.edgeProximity + car.edgeSide exposed for the renderer
V006
★ slow-mo wipeout ★
▶ PLAY V006

PROMPT

ok when we crash we should see a spectacular explosion in slow motion

CHANGES

  • Crash sequence runs in slow-mo for 2.4 real seconds via a global timeScale (curve 0.10 + t^2.6 · 0.90)
  • Car shatters into ~17 rotating line-shard debris pieces flying outward, each with phosphor-dot endpoints
  • 90 radial sparks (red / yellow / orange / white) with halos + hot cores
  • 5 staggered shockwave rings, each with halo + mid-glow + crisp ring passes
  • Initial radial bloom (white→amber→red), then darkening vignette while debris flies
  • WIPEOUT title + frozen final score in Press Start 2P, mid-screen
  • HUD score hidden during the crash so it doesn't fight the in-canvas readout
  • commitReset() regenerates the world after the spectacle ends
V005
★ score + plateau widths ★
▶ PLAY V005

PROMPT

ok vary the size of the track more in dimension although we can have lon stretches the same size, include the score in a font reminiscent of asteroids, top centre larger than the distance font, the score should be speed multiplied by distance, use commas if the score gets large.

CHANGES

  • Width range expanded to 38–230; double-smoothstep on a slow noise (~5500u wavelength) gives long flat plateaus that snap between bands
  • Spawn area clamped to ≥55% of max width so you never start in a pinch
  • Press Start 2P (Google Fonts) loaded for the score readout
  • Score = floor(car.speed × distance), formatted via toLocaleString("en-US") for commas
  • SCORE element top-center, clamp(26px, 4.4vw, 56px) with cyan triple-glow
  • BEST now tracks max score (new infiniracer.bestScore localStorage key) and is also comma-formatted
V004
★ joystick + new keys ★
▶ PLAY V004

PROMPT

in the bottom fith of the screen show a joystick I can use to control the ship with the mouse, also change the lettered controls to q up a brake o left and p right

CHANGES

  • Vector-styled virtual joystick in the bottom fifth — drag with mouse or touch, knob springs back on release
  • Outer halo ring + cardinal phosphor ticks + amber stem + hot core knob; THROTTLE / BRAKE / ◀ STEER ▶ labels
  • Keyboard remap: throttle Q/, brake A/, left O/, right P/
  • Unified analog input pipeline (readInput()) — keyboard ORed with stick magnitude per axis
  • Thrust flame intensity now scales with analog throttle
  • Cursor changed back from none to crosshair; touch-action: none to stop mobile fighting the stick
  • Start overlay updated with the new controls + "OR DRAG THE JOYSTICK"
V003
★ much faster + variable width ★
▶ PLAY V003

PROMPT

ok allow the player to go much much faster and alter the width of the track randomly. when the player goes off the track reset the distance

CHANGES

  • MAX_SPEED 620 → 1600, ACCEL 140 → 280, BRAKE_DECEL 320 → 520
  • Per-segment hw (50–160) driven by two slow noise octaves; spawn clamped wide
  • Off-track now triggers a full run reset — world seed re-randomized, track + scenery cleared, car teleported home
  • Brief red/white WIPEOUT flash with text on reset; best distance preserved
  • Distance markers + scenery offsets switched from the old constant to per-segment seg.hw
V002
★ scaling car + faster ★
▶ PLAY V002

PROMPT

ok cool, the vehicle should also alter size when zooming out and make it possible to go faster

CHANGES

  • Car geometry scaled by world zoom in p(x, y) so the ship shrinks as the camera pulls back; line widths stay constant (vector-tube convention)
  • MAX_SPEED 360 → 620, ACCEL 95 → 140, BRAKE_DECEL 220 → 320
  • Off-road drag rebalanced to BRAKE_DECEL · 0.55 so it still bites at the new top speed
V001
★ genesis ★
▶ PLAY V001

PROMPT

ok in v001 dir create a game using only js html and css, the game is infini racer where you control a vehicle through a landscape that is purposefully minimal, in the vector style of the original asteroid game from the 70s/80s. where lines on landscape object or vehicle intersect there is a more brilliant point or dot. This is a top down racer where the vehicle appears static but the course moves and zooms out the faster you go. You can also look at other vector/retro games for inspiration.

CHANGES

  • Vector top-down racer: car drawn at screen-center pointing up; the world rotates + translates around it
  • Procedural infinite track from two octaves of smooth noise; deterministic chunked scenery (3–6-sided wireframes)
  • 3-pass beam rendering — wide soft glow → medium glow → crisp line — for a phosphor-tube feel
  • Phosphor endpoint dots at every line endpoint plus extra-bright cross-group line-intersection dots
  • Camera zoom-out as 1.05 / (1 + speed_ratio · 1.35)
  • CRT scanline overlay + radial vignette
  • Off-road drag, distance + speed HUD, best distance in localStorage