# Ectobounce

![icon](pwa-512x512.png "Ectobounce Icon")

Catch ghosts. Pass the chaos. A strictly 2D, single-screen arcade platform game for **1–6 players**, with direct touch play or separate phone controllers.

## Screenshots

Six-player action in the Moonlit Lobby.

![Six players jumping, catching and launching ghosts in the Moonlit Lobby](screenshot01.jpg "Ectobounce — six-player gameplay")

The Glasshouse of Whispers brings a new backdrop and more ghost varieties.

![Six-player gameplay in the Glasshouse of Whispers](screenshot02.jpg "Ectobounce — Glasshouse of Whispers")

Portrait play with movement, jump and zap controls on the same screen.

![Portrait gameplay with built-in touch controls below the arena](screenshot03.jpg "Ectobounce — touch gameplay")

## Play

The illustrated lobby guide explains the sequence: face a nearby ghost and press **Zap** to stun it, move into it with an empty lantern to capture it, then press **Launch** to fire it at other ghosts. Zap and Launch share the same button (X or Z on a keyboard). Aim by moving left or right, and launch within five seconds. The guide is also available in Settings and on each controller's first check-in and Menu.

Enter **Your name** in the lobby or Settings. Controller players can change their name in Menu. Names use the shared `rcwebName` local-storage key, are limited to 16 characters, and update the current player without losing their slot or score. If browser storage is unavailable, the name still works for the current page and the field reports that it cannot be saved.

On a smartphone, open **`/ectobounce/`** and tap **Play here**. Fixed bottom controls move, jump and zap/launch; in spirit form, Jump and Zap become Fly Up and Fly Down. Touch play shares one local slot with the keyboard, leaving up to five slots for friends. The arena reserves space above the controls. The touch layout follows the visual viewport as browser bars move, uses safe-area padding for notches/home indicators, and falls back to `innerHeight` on older browsers. Rotating during a wave preserves its layout and match; the arena orientation changes between waves. Use a separate room if another viewer already hosts the current room.

Open **`/ectobounce/`** on the shared screen and scan its QR code with each phone. Controllers use **`/ectobounce-c/`** in the same RCWeb room. The first connected player is captain and can start the game from their phone's Menu. Use a screen address reachable by phones; `localhost` on a phone points to that phone, not the TV or server.

After victory or defeat, every joined controller can choose **Play again** to restart from room 1, including when the viewer joined first. The viewer's end-of-game overlay closes automatically when the new game starts, and the crew keeps its existing player slots and connections.

Lobby and in-game invitations use the standard RCWeb QR panel, including the host header and room-ID footer. At widths up to 700 pixels, an Invite players link opens that panel in a modal with a Copy link button and selectable controller URL. Wider displays retain their inline QR panels.

- Left/right: move. Jump: hold for height, release for a shorter hop.
- Zap a nearby ghost to stun it, then touch it with an empty lantern to catch it.
- The same button becomes Launch. Fire the captive through other ghosts; wall rebounds set up bank shots.
- An empty-lantern teammate can catch your shot and pass it onward. Chains reach 4x. Passing by itself awards no points.
- Touch a teammate's spirit to rescue them. Otherwise they return automatically within five seconds.
- Clear ten waves before the shared team-spirit meter runs out. New arrivals enter the current wave immediately with two seconds of protection, up to six simultaneous players; a fresh crew can take over if everyone leaves.
- Captives automatically eject after five seconds. Passed shots keep their finite remaining lifetime, including time spent held.

On the shared screen, use the gamepad join button or press **K** to enter at any time, including during a wave. The keyboard uses one of the six player spaces alongside phone players; joining preserves the current wave, scores and pause state. Arrows or A/D move, Space/Up/W jumps, and X/Z zaps or launches. The lobby's **Try with keyboard** button also starts a solo practice session.

## Art and animation

Original imagegen artwork includes ten individually painted rooms, separate gold-edged balconies, a coral-hooded lantern catcher, three expressive ghost species, ghost balls and magical effects. The run sheet has 16 poses, the action sheet 16 (idle, jump, attack, hurt/recovery), the detailed ghost sheet 48 (twelve per species and twelve ghost-ball poses), and the effect sheet 16: **96 authored animation frames**. The new ghosts match the catcher with layered cloth, textured shading, warm rim highlights and expressive dark faces. Movement is simulated at a fixed 60 Hz. Six numbered colour identities are prepared once at load time; the lantern retains its amber light.

Every wave visits a different location: Moonlit Lobby, Whispering Library, Glasshouse of Whispers, Midnight Kitchen, Forgotten Ballroom, Hall of Forgotten Portraits, Drowned Bathhouse, Thirteenth Hour, Astral Observatory and The Last Seance. `scenes.js` defines this order. The next room preloads while the current wave plays; only current/next decoded images are retained. Lighting is painted into the compressed 1280x720 JPEGs, and the room/platform composite is redrawn only on room, size or quality changes. A failed room image produces a visible message and keeps the previous room playable; reload to retry. Replay returns to the lobby.

The runtime uses local PNG atlases, a cached painted room and tightly bounded light/effect sprites. Standard mode adds lantern glows, shot trails and drifting dust. Lean mode retains the artwork and all gameplay, removes optional ambient lighting/trails, and targets 30 rendered frames per second. Standard rendering targets 60. Neither mode requires WebGL, CSS filters, external fonts or a graphics library. Actual older-TV frame rates must be verified on the target device.

The game keeps the full landscape arena visible at 1920x1080 and 960x540. Portrait uses its own connected platform arrangement at 1080x1920 and 450x960. If a display rotates during a wave, the current arena is fitted until the next intermission, when the arrangement changes.

All ten rooms have different platform layouts in both landscape and portrait. Alternating library shelves, glasshouse islands, ballroom terraces and the clock room's staircase change the routes through the hotel. Every platform is reachable using the normal jump. Room changes place the crew safely on the floor and preserve their scores; replay restores the first layout. Platforms and the backdrop are painted into the same cache, so layout variety adds no ongoing drawing cost.

## Original audio

Each room has its own original score with a different melody, tempo, harmony and arrangement. **Midnight Check-in**, the lobby's music-box waltz, now runs at 152 BPM. All ten scores use a fast 144–168 BPM arcade pace, with tighter lead notes and rhythmic pulses behind the spooky harmonies. Nine 16-bar scores follow it:

- **Moonlit Lobby:** Midnight Check-in — 152 bpm. Music box, organ and plucked bass; 3/4 waltz.
- **Whispering Library:** Footnotes After Dark — 148 bpm. Quiet harp figures, deliberate rests and airy chords.
- **Glasshouse of Whispers:** Moonflowers — 150 bpm. Rising glass chimes, high answering phrases and floating harmony.
- **Midnight Kitchen:** Pots, Pans and Poltergeists — 160 bpm. Syncopated marimba, woody taps and bouncing bass.
- **Forgotten Ballroom:** The Guests Who Never Left — 156 bpm. Haunted reed waltz, bowed chords and offbeat harp accompaniment.
- **Hall of Forgotten Portraits:** Eyes Behind the Frames — 152 bpm. Sparse, echoing music-box phrases over dark bowed harmony.
- **Drowned Bathhouse:** Ripples Below Midnight — 144 bpm. Descending glass tones and overlapping watery echoes.
- **Thirteenth Hour:** One Minute to Thirteen — 168 bpm. Dry plucked arpeggios and alternating clock ticks.
- **Astral Observatory:** Orbits of the Unseen — 148 bpm. Widely spaced celestial chimes and slow, airy chords.
- **The Last Seance:** The Circle Closes — 164 bpm. Insistent organ melody, bowed harmony and ritual bass pulses.

The browser generates mono 22,050 Hz playback buffers with a short-lived Web Worker; browsers without a working worker use small, time-limited synthesis steps. Only the current and next scores are cached. The next score prepares while the current one plays; room changes use a 0.6-second crossfade with at most two music voices. If preparation is delayed, the current music continues until the new score is ready. Obsolete work is cancelled when skipping rooms or restarting. Muting silences both fade voices and remains in effect across room changes; enabling sound late selects that room directly. Pause/resume retains the current loop, and replay selects the lobby score.

After generation, native audio buffers handle playback with no ongoing musical synthesis. There are **no WAV, MP3 or other audio-file downloads**. The previous 2,940,242-byte WAV pair is archived under `output/ectobounce/production-source/audio/` for reference.

Seventeen generated event sounds cover jump, landing, zap, stun, catch, launch, rebound, banish, star pickup, pass, rescue, hit, wave start, room clear, victory, defeat and shot expiry. Game events trigger them, with pitch rising on chains and a bounded effect voice count. The score and synthesis are original; no downloaded music or sound library is used.

Open **Settings** and tap **Enable sound** on the display (or press Enter there) to satisfy browser audio-interaction requirements. A remote phone cannot grant the display's audio permission. Music and effects have separate switches. The button displays **Preparing sound…** during initial synthesis; music and effects become available together when it finishes. Gameplay and existing sound effects continue during later score preparation. Preparation takes longer on slow devices, especially without a worker. Worker failure is reported before the incremental fallback; generation failures preserve working audio and provide retry instructions. Current/next scores plus the shared effect buffer remain below 10 MB in the tested room sequence; synthesis and an outgoing fade temporarily require additional memory. All ten full tracks are never retained together.

## Interface

The lobby has one primary Play here button, a painted bellhop illustration, and a separate invitation action. Start crew appears only when remote players are waiting and the display has not joined locally. Touch controls are hidden until the game begins. During play, an icon toolbar provides invitations, pause and Settings; the room counter and segmented team-spirit meter replace long status labels. Sound, music and graphics options live in Settings. The touch buttons use vector arrows, a jump arc and a bolt, with short action labels and pressed-state feedback. Keyboard focus rings appear for keyboard navigation.

The UI reuses the existing painted character and local SVG icons; no new fonts, libraries, generated assets or runtime effects are required. Flat fills and the existing lean path support older screens.

## Room behaviour

One viewer is authoritative. Additional viewers visibly defer to the existing host. Phones send coalesced input changes and short state heartbeats; counters preserve brief button presses and sessions reject commands queued before reconnecting. Lost input releases movement after 350 ms. Reloading or reconnecting a controller retains the player's slot and score for the 20-second presence window. Newly opened controller tabs get separate player identities, even when opened from another controller. A seventh phone receives a room-full waiting state.

A viewer network interruption pauses the arena. A full viewer reload begins a new lobby; exact in-flight match recovery and mirrored spectator displays are not implemented. RCWeb is a trusted-room execution environment; app validation is not a security boundary against malicious room members.

## Files and checks

- `engine.js`: deterministic movement, collisions, combat, scoring and waves.
- `graphics.js`, `scenes.js`: Canvas renderer, wave backgrounds, sprite animation and palette preparation.
- `audio.js`, `music-scores.js`, `sound-synth.js`, `sound-worker.js`: ten original room scores, local synthesis, preloading, crossfades and bounded event playback.
- `script.js`: viewer UI, room hosting and fixed-step timing.
- `interface.browser.test.cjs`: single play action, Settings focus and keyboard isolation, icon bounds, pause/resume and lean layouts across eight screen sizes.
- `touch-controls.js`: optional direct touch input and visible-viewport layout; no additional dependencies. Mouse/keyboard displays and Tizen retain the shared-screen layout.
- `touch.browser.test.cjs`: direct play, simultaneous input, release/cancel/blur, ghost flight, phone sizes, rotation, simulated browser chrome/safe areas and the legacy viewport fallback.
- `engine.test.cjs`: behaviour tests; run `node src/main/apps/app/ectobounce/engine.test.cjs` from the repository root.
- `browser.test.cjs`: six-controller integration checks against localhost:8080, using the existing bundled Playwright and Chrome installation.
- `multiplayer.test.cjs`: six concurrent touch controllers, mid-wave joining, copied tabs, capacity and reconnect regression checks against localhost:8080.
- `keyboard.test.cjs`: keyboard joining during play, pause and intermission; five phones plus keyboard; input, repeated joining and toolbar layout checks.
- `qr.test.cjs`: comparison with the standard `/v/` QR panel, label visibility, layout and working controller links in both display modes at all four target sizes.
- `graphics.test.cjs`: wave image caching, restart, stale image completion, visible failure and all 48 ghost-frame bounds.
- `audio.test.cjs`: ten distinct melodies/loops, deterministic PCM, effect envelopes, crossfades, current/next caching, stale/cancelled jobs, retry, playback toggles, chain pitch and voice limits.
- `room-music.test.cjs`: real worker/audio playback through all ten rooms, mute/replay/late enable, shared effects and six-player cooperative preparation under CPU slowdown.
- `refresh.test.cjs`: all ten rooms in the browser, zero audio downloads, worker/no-worker/failed-worker audio preparation and responsive input under CPU slowdown.
- `art-prompts.md`: imagegen prompts and production notes.
- `music-source.cjs`: optional reference WAV export into the production-source archive; unused by the game.

Source concept art and generation/QA working files are saved under `output/ectobounce/`. New full-resolution rooms and transparent ghost masters are preserved in `output/ectobounce/production-source/refresh/`; runtime files are in `assets/`. Java backend and shared communications files are unchanged.
