Moving platforms reversed velocity whenever |x-startX|>range, which stays true for several frames after the flip, so direction flipped every frame and the platform vibrated in place. Now reverse only when past the edge AND still heading outward. Same directional guard for enemy 'bug' patrol. Added a 6px deadzone to 'mev_bot' so it stops shaking when aligned with the player. Verified: a moving platform now flips ~4x over 360 frames and travels the full range symmetrically.
Replace the flat line-art backgrounds with 6 rich, full-screen artworks
painted via the Canvas2D API (linear/radial gradients, screen/lighter
blending, soft glows, bokeh, stars) and registered as Phaser canvas textures:
- Nebula: colorful deep-space clouds (default)
- Aurora: northern lights over a starfield
- Sunset: synthwave sky with glowing sun + perspective grid
- Ocean: sunlit underwater depths with caustics and bubbles
- Dreamscape: soft pastel bokeh
- Monad: signature purple cosmos
- Backgrounds are now fixed full-screen images (no tilePositionY scroll,
which would seam a gradient); motion comes from platforms + gwei particles
- Settings background preview switched to a scaled image showing the whole art
- Verified all 6 in-browser (menu, in-game, settings preview), no console errors
- 6 procedural, vertically-tiling backgrounds generated in BootScene:
Grid, Hex Nodes, Starfield, Synthwave, Circuit, Void
- config/backgrounds.js registry + utils/background.js helper; selection
persisted via storage (KEYS.background)
- Menu / Game / GameOver use the selected background texture
- Settings overlay gains a BACKGROUND selector: in-panel live preview tile
+ < name > cycling, updates the menu background live and persists choice
- Fix: stepper/arrow buttons now add both bg and label to the modal so the
-, +, <, > glyphs render above the panel
B1 Seeded RNG + Daily Challenge
- utils/random.js wraps Phaser seedable RND (Between/frac are NOT seedable)
- All gameplay spawning (PlatformManager, Platform, Enemy) uses seeded rng
- GameScene reads mode/seed in init and seeds the run; daily shows a HUD badge
and keeps a per-day best (daily_<YYYYMMDD>); MenuScene DAILY button;
GameOver RETRY preserves mode and shows today's best
- Verified: same seed -> identical layout, different seed -> different
B2 New content
- Enemy mev_bot: homing chaser that eases toward the player (unlock >1500)
- Platform reorg: phantom, semi-transparent, vanishes shortly after landing
(unlock >600); no power-ups on breaking/reorg; SPAWN_RATES + UNLOCK config
- Verified spawn distribution at high difficulty includes all new types
B3 Settings
- SoundManager gains volume (persisted); MenuScene SETTINGS overlay with
volume stepper, particle-quality Low/High toggle, two-step reset progress
B4 Stats
- StatsManager tracks lifetime games/jumps/stomps/blocks/best combo, flushed
at game over; MenuScene STATS overlay; hooks in GameScene/ScoreManager
B5 Difficulty tuning via UNLOCK thresholds and rebalanced spawn rates
Functionally verified in-browser via eval (no console errors, deterministic
daily, content spawns, particles emit). Visual screenshot unavailable in the
headless preview because the hidden tab pauses Phaser's loop.
A1 Pooled particle system
- New ParticleManager owns 9 reusable Phaser emitters created once per scene
(jump, explosion, powerup, puff, sparks, flame, wind, spring, speedline)
- BootScene generates reusable white textures (px_square/soft/streak/ring)
- GameScene burst helpers + EffectsManager flow effects now delegate to the
pool instead of allocating rectangles + tweens every frame
- Quality auto-detect (low on mobile/small screens) cuts particle counts
A2 Fix high-speed landing tunneling
- Cap downward velocity at PHYSICS.maxFallSpeed (boosts unaffected)
- platformCollisionFilter now uses a swept deltaY one-way check so a fast
fall can never be wrongly rejected or passed through
A3 Safe storage
- New utils/storage.js wraps localStorage with in-memory fallback so private
mode / quota errors cannot crash the game; all access routed through it
A4-A6 Lifecycle and robustness
- Global error / unhandledrejection guard recovers to the menu
- GameScene auto-pauses on tab hidden and cleans up the cross-scene listener
on shutdown; fps pacing + disableContextMenu in game config
- Moving platforms use a proper dynamic body instead of destroy()+new Body
Verified in-browser: menu + game load with zero console errors, all emitters
active, normal bouncing works, fall speed capped.
- Camera now uses a trigger line at 42% from top. Player rises/falls
freely below the line; camera only scrolls up past it, never down.
- Remove playerShadow graphics that followed the hero around.
- Ignore .vercel-cli-config (CLI auth cache used as Windows workaround).
Root cause: startFollow used lerpY=0.05 + roundPixels=true. At rocket
velocity (~1400 px/s) the camera lagged ~466px behind the player, then
snapped in chunks every frame, giving the impression of low FPS / shake.
Fix:
- Replace startFollow with manual upward-only camera tracking that
matches the player exactly (doodle-jump standard behavior)
- Disable camera roundPixels so antialiasing actually smooths motion
- Round bg tilePositionY to integer to avoid grid texture shimmer
- Slightly thin out boost effects (flame 3->2/frame, speed line 40->65ms)
to keep frame budget headroom on weaker devices
- EffectsManager handles all powerup visual feedback in one place
- Replace circle trail with proper effects per type:
* Rocket: yellow->red flame particles from feet + occasional bright spark
* Propeller: white/blue wind streaks around player
* Spring: gold/green spark trail while ascending fast
- Boost start burst: two expanding rings + radial sparks + camera shake
(orange/gold for rocket, cyan for propeller, green/gold for spring)
- Boost end puff: gray smoke cloud spreading from player
- Screen edge speed lines during boost (alternate left/right edges)
- Player no longer tilts with horizontal input during boost; rocket
stays rigid upright with tiny lean, propeller has gentle sinusoidal wobble
- Extract createButton/pixelText helpers to src/utils/ui.js with
sensible defaults and per-call option overrides
- MenuScene now shows BADGES button opening the achievement panel
(10 entries, count of unlocked, star icons for completed)
- GameOverScene buttons migrated to shared utility, removing duplicate
hover/click handlers
- Smaller LEADERBOARD button to make room for BADGES alongside
- AchievementsManager with 10 unlockables and toast popups
(Genesis Block, Chain Reaction x3, Bug Hunter, First Flight,
Liftoff, Power Trip, Survivor 500, Skyscraper 1000,
Speedrun 100/60s, Gas Baron 50k)
- Score popups: +N text floats above player on every landing
(gold and larger for genesis platforms)
- Powerup duration bar in HUD bottom, color-coded per power-up,
uses scaleX for smooth depletion animation
- New enemy: Failed Tx, falls from above with sine drift, unlocks
at difficulty > 800, can be stomped, tinted red
- Dynamic background: dark cosmic overlay alpha scales with height
(max 0.5 at very high altitudes)
- Achievement hooks integrated into ScoreManager, GameScene
- Combo no longer resets if combo was already 0 (was triggering log spam)
- SoundManager via Web Audio API (no asset files needed) with procedural
SFX for jump, spring, powerup, stomp, break, death, milestone, new-best
- Sound persists mute state in localStorage; mute button on Menu and Game
- Pause system: ESC key or onscreen pause button, modal overlay with
Resume and Main Menu options, physics correctly paused/resumed
- First-run tutorial overlay explaining controls and platform types,
dismissed and remembered via localStorage flag
- Touch indicator hints fade after 3.5s on touch devices only
- Menu start triggers AudioContext initialization (browser autoplay rules)
- GameOverScene supports ENTER/SPACE shortcut for retry, NEW BEST text
now pulses, sounds fire on each transition
- Fix Rocket/PropellerHat hitboxes (relative to sprite size, not magic offsets)
- Spring now destroys itself on use with squash animation instead of staying invisible
- Guard all powerups against double-trigger via consumed flag
- Save genesis glow tween reference for clean destroy
- Player.die() disables collisions (no more post-death platform bounces)
- Replace hardcoded enemy spawn cap 0.5 with DIFFICULTY.maxEnemyRate
- Enemy spawn position now anchored to platform X (-60/+60), not random
- Powerup spawn clamped to screen bounds
- Skip powerup spawn on breaking platforms (was unfair)
- ScoreManager.addPoints() public method; remove direct score mutation
- HUD elements use setScrollFactor(0) instead of per-frame repositioning
- All magic numbers extracted to SCORE / PHYSICS / POWERUP_DURATION config
- Remove dead code: showDeathVignette, drawDebug, createParticles, deathOverlay
- Remove dead constants: PLATFORM_WIDTH/HEIGHT, PLAYER_MAX_SPEED, tileBias
- Remove SUBMIT TO CHAIN fake button, replace with Coming Soon text
- Safer cleanup loop: collect-then-destroy instead of mutating during iterate
- gwei particles now have setScrollFactor(0.3) for parallax depth