Hexapuss Portal Tap: Game Design Document
Title: Hexapuss Portal Tap
Spelling: Hexapuss (two s)
Owner: Steve Hinan
Doc date: 2026-09-18
Build: HTML5 static web (no npm / no build step)
Package: COMBINED v11 (10 circles + white win + cosmic horror synth + fastest-clear LB)
1. Concept / fantasy
The player feeds a living Hexapuss portal by tapping occult magic circles. The reward video (cat eye shifting Orange → Green → Purple, then a black pupil, hexagonal tunnel, and white bloom) advances on successful taps. Lore colors: orange, green, purple for circles 1-9; circle 10 is WHITE (win). Clear all ten taps and the portal bloom completes.
Tone: dark occult / cosmic, mobile portrait, touch-first. Player-facing copy avoids em dashes.
Player-facing win subline: Ten sigils complete (not “circles”). Never show end tagline “The rift opens.”
2. Platform and aspect
| Item | Spec |
|---|---|
| Runtime | HTML5 + CSS + vanilla JS |
| Orientation | Mobile-first portrait |
| Layout | 100dvh, safe-area insets, touch-action: manipulation |
| Desktop | Centered phone column (~max 480px), same portrait rules |
| Input | Pointer events (touch + mouse) |
| Audio | cosmicHorrorSynth.js Web Audio drone + escalate; soft reward blips |
3. Title flow
- Full-screen dark welcome.
- Official Hexapuss logo (
assets/hexapuss-logo-official.png) acts as the play control, with a clear PLAY affordance under it. - Subtitle:
PORTAL TAP. Short tagline under that. - On Play (user gesture):
- Switch to game screen.
- Start cosmic horror ambient synth.
- Hold full black.
- Start video/reward.mp4 (gesture unlocks media playback).
- Fade black out so the video reveals.
Play Again starts a new run immediately (same as title Play). Title is first-load only; after a clear, stay on the Play Again end screen until Play Again.
4. Video structure and time markers
Asset: video/reward.mp4 (portrait ~404×720, H.264, ~16s).
| Constant | Value | Meaning |
|---|---|---|
INTRO_END | 4.0 | Start hold / pure full-screen black after cat-eye zoom. Circle 1 may appear. |
BLOOM_START | 14.0 | End hold for circle 10 (white bloom). |
VIDEO_END | video.duration | Natural end (~15.97s). Win overlay after outro. |
Even splits (locked)
- Circle 1 owns the start hold (
0→INTRO_END). Intro plays free; tap unlocks progress. - Circles 2-9 get even splits of
[INTRO_END, BLOOM_START)(8 equal slices). Each successful tap seeks to that slice and plays to its end, then pauses. - Circle 10 owns the end hold: seeks near end /
BLOOM_START, plays completion through bloom, then win UI.
5. Circle rules (10 total)
| Rule | Behavior |
|---|---|
| Count | 10 successful taps to clear |
| Colors 1-9 | Cycle orange → green → purple (circle n → (n-1) % 3) |
| Circle 10 | WHITE (win) |
| Live targets | MAX_CONCURRENT = 1: short-TTL circle respawns while waiting. Never two live. |
| Miss / expire | Untapped circle is hard-cut from the DOM (no fade / fizz / ripple) |
| Hit feedback | Brief hit scale/opacity; colored flash; purple stronger; white-10 win flash |
HUD: Circle X / 10 + phase name (incl. White).
6. Cosmic horror synth
File: cosmicHorrorSynth.js → global CosmicHorrorSynth.
| Hook | Behavior |
|---|---|
startAmbient() | On PLAY: louder drone + mid/high cosmic-horror layers, slow LFOs, psychedelic delay/pan |
onCircle(n, {video}) | Escalate intensity for 1-9; distinct white-10 climax |
setVideoActive(bool) | Unmute while reward video playing; mute while paused |
stop() | Hard stop on win / game over (no leftover drone) |
Game wires onCircle on every successful tap, starts ambient on PLAY, gates synth to video play/pause, and hard-stops on win. Magic SFX fire on every circle spawn and every death (TTL miss included).
7. Win / outro / end screen
After circle 10 (white):
- Stop spawning; clear live targets.
- Full-screen pure white bloom (covers letterboxing) → then full black end UI.
- Synth hard-stops on win / game over (no leftover drone).
- End / Play Again screen (title-like polish):
- Hexapuss logo (blacks match center blacks; no purple wash under the mark)
- Same purple edge glow as title/start (.edge-glow: outer screen edges only)
- Title line: PORTAL CLEARED
- Subline (player-facing): Ten sigils complete (say sigils, not circles)
- Do not show the old end tagline “The rift opens.” (removed entirely)
- Clear time (kept)
- FASTEST CLEARS leaderboard + PLAY AGAIN
- Player name is edited inline in the green is-you row (name field + OK in that cell only). No separate NAME UI above/beside the board.
- End-screen text color = orange family. Green ONLY for the current player’s leaderboard row highlight (
is-you). Everything else on the end screen stays orange. - Play Again starts a new run immediately (same as title Play). No bounce back to the title screen.
- Title / start screen = first load only. After a clear, stay on the Play Again end screen until Play Again.
No lose state (no timer fail).
Fastest-clear leaderboard
| Item | Spec |
|---|---|
| Metric | Fastest clear time (lower clearTimeMs is better; ASC) |
| Timing start | beginGameplay after INTRO_END (~4.0s pure full-screen black pupil) |
| Timing end | On the 10th successful tap |
| Display | Clear time on win (orange); current run row green highlight (is-you) with inline name + OK |
| Shared board | leaderboard/scores.ashx (Meteor Attack pattern): GET / POST {name, clearTimeMs, runId}; upsert by runId keeping better (lower) time; max 15; ascending |
| Fallback | localStorage keys hexapuss-portal-tap-scores / hexapuss-portal-tap-name when ashx unavailable (Desktop file://) |
| Ship note | Do not check in scores.json; created on first server write |
8. Asset list and paths
| Path | Role |
|---|---|
index.html | Shell / screens |
style.css | Portrait layout, O/G/P + white phase, flash, win, LB |
game.js | Flow, 10 circles, video splits, LB, synth wiring |
cosmicHorrorSynth.js | Ambient drone + escalate + white climax |
leaderboard/scores.ashx | Fastest-clear shared board (IIS/ASP.NET) |
art/portal-sigil.png | Magic-circle tap target |
video/reward.mp4 | Background reward / outro video |
assets/hexapuss-logo-official.png | Title play logo |
Play Hexapuss Portal Tap.bat | Windows helper: opens index.html |
PORTAL-TAP-GDD.md | This document |
README.md | Short play / file notes |
9. Open tuning knobs
Edit constants at the top of game.js:
| Knob | Default | Notes |
|---|---|---|
INTRO_END | 4.0 | Start hold / pure black / gameplay begin |
BLOOM_START | 14.0 | End hold for circle 10 |
CIRCLE_SIZE | 110 | Tap target diameter (px) |
CIRCLE_TTL_MIN / MAX | 450 / 750 ms | Per-circle life; then instant DOM remove |
SPAWN_GAP_MIN / MAX | 120 / 220 ms | Delay before next spawn attempt |
MAX_CONCURRENT | 1 | Only one live circle |
HIT_CLEAR_MS | 200 | Brief hit feedback before remove |
TOTAL_CIRCLES | 10 | Win on 10th tap |
10. Screen map
[Title] --Play--> [Black] --> [Video intro 0..4.0s] + ambient synth
|
v
[Circle 1..9 O/G/P + even video 2-9]
|
(circle 10 WHITE)
v
[Outro bloom .. VIDEO_END]
|
v
[Win + clear time + LB]
|
Play Again --> [new run immediately]
Title appears on first load only. After a clear, the Play Again end screen stays until Play Again.
11. Success criteria (acceptance)
- Dark title with Hexapuss logo + PLAY.
- Play → ambient synth + black → video reveals.
- After ~4.0s, one short-TTL circle at a time (hard-cut on miss).
- Circles 1-9 cycle orange/green/purple; circle 10 is white.
- Video even-splits for taps 2-9; tap 10 seeks ~end and completes.
- Synth escalates 1-9; white climax on 10.
- Clear timer = gameplay start → 10th tap; LB ASC with name + highlight.
- End screen: Hexapuss logo + same purple edge glow as title; no “The rift opens.”; subline Ten sigils complete; clear time kept; orange text; green only on current LB row; Play Again starts a new run immediately.
- Synth louder with mid/high content; gated to video motion; hard-stop on win.
- INTRO_END at pure black (~4.0s); full-screen white bloom before black win UI.
- No CSS ring on magic circles (sigil art only); purple edge glow on title + Play Again only (not under logo).
- Magic SFX on spawn and death.
12. Publish / next
- Live play URL: https://stevehinanpresents.com/portal-tap/
- Desktop playtest:
C:\Users\Steve\Desktop\Hexapuss Portal Tap\+Play Hexapuss Portal Tap.bat - Box source:
/workspace/hexapuss-portal-tap/ - Public GDD (Phil): experimental site page under
experimental/gdd/portal-tap/(linked from Experimental). One GDD per game. - Shared board:
leaderboard/scores.ashxon IIS; never overwrite livescores.jsonon later uploads; grant Write onleaderboard/. - Parked for NEXT build (not in this doc as shipped): early-circle visibility dig on live; higher-pitched success ding on tap.
13. Changelog
| Date | Change |
|---|---|
| 2026-09-18 | Public GDD pass: inline green-row name; live /portal-tap/; Experimental GDD link path. |
| 2026-09-17 | Circle layering: #screen-game.is-gameplay (circles above blackout); white-bloom idle z-index 3. |
| 2026-09-17 | Quinn polish: white bloom hold before is-out; .edge-glow CSS; synth gated off until play; soft-cap via seekVideoTo; INTRO_END 4.0 scrub-confirmed. |
| 2026-09-17 | Harden video: seekVideoTo + mediaBusy for mid-run seeks; ensureVideoPlaying + stalled/canplay recovery. |
| 2026-09-17 | Fix: Play / Play Again intro stuck-pause: wait for seek-to-0, introSeeking gate, cancel finishWin timer, readyState canplay. |
| 2026-09-17 | End screen: remove “The rift opens.”; “Ten sigils complete”; orange text; green only for current LB row; purple edge glow like title. |
| 2026-09-17 | COMBINED v11: 10 circles (O/G/P 1-9 + white 10); even video splits 2-9; cosmicHorrorSynth; LB end on 10th tap; keep Mel ashx/localStorage. |
| 2026-09-17 | Fastest-clear leaderboard: ashx + localStorage; Desktop-first. |
| 2026-09-17 | Playtest: MAX_CONCURRENT=1; TTL miss = instant DOM remove. |
| 2026-09-17 | Locked design rebuild from score-attack prototype. |