Skip to content
rvenningPublic

About

Shared components for the family games: profiles, PINs, Firestore sync, WebAudio synth, PWA kit.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

gamekit

Shared components for the family games (WordVoyage, Chicken Cross, and whatever comes next). Plain scripts, no build step — everything hangs off a single GK global.

▶ Play the demo — a tiny reflex game that exercises every component; read its source as living documentation.

What's in the box

File What it does
gk/gk-util.js helpers: esc, clamp, lerp, rand, pick, shade, hash2, plus seededRand/seedFrom — a reproducible RNG for daily challenges and deterministic tests
gk/gk-audio.js GK.Sfx — WebAudio synth (tone, noise) + default click/coin/win/lose/wrong; games add their own jingles. Wakes its own AudioContext on the first gesture, so menus are audible without the game calling init()
gk/gk-ui.js GK.UI — screens (.screen + #screen-NAME), modals (.modal.visible), toast, sound toggle, and bindMenuClicks() — one delegated listener that clicks any menu button whose own handler stayed silent
gk/gk-storage.js GK.createStorage(cfg) — localStorage persistence + optional Firestore family sync (profiles, progress, tombstoned deletes; progress writes debounced, flushed on tab hide/close)
gk/gk-profiles.js GK.Profiles — emoji-avatar roster, 4-digit PINs with admin override, type-name delete, leaderboard renderer; injects its own modals
gk/gk-pwa.js GK.initPWA() — service-worker registration + Add-to-Home-Screen button (beforeinstallprompt on Chrome, instructions modal on iOS). Also GK.Version: shows "Version 12 · 14 Sep 2026" at the foot of the start-up screen, then "✓ Latest" or an Update button by comparing the page's <meta name="gk-version"> with the deployed version.json
gk/gk-fx.js GK.Fx — canvas juice: pooled particles (burst/trail/dust/sparkle/splash/confetti), screen shake, flash, floating text, lightning, slow-mo; plus GK.Tween. Per-game feel via GK.Fx.configure({...})
gk/gk-debug.js GK.Debug — dev tools behind ?debug=1: floating panel, FPS, toggle/action/jump controls games register themselves. Suppresses progress writes while on
gk/gk-path.js GK.Route + GK.Corridor — levels authored as waypoints instead of tile maps, plus the linters that keep them honest. A route is a polyline you walk by distance (Turret Town's roads); a corridor is a centre-line-and-width profile you sample across (Rocket Rescue's caves), with place(x, t) for positioning content by a fraction of the passage so it can never land inside solid geometry
gk/gk-base.css shared styles for all of the above, themed via --gk-* custom properties (button lettering is --gk-btn-ink, defaulting to --gk-ink; set it to #fff if your game's accent is dark); also makes the hidden attribute authoritative ([hidden] { display:none !important }) so el.hidden = true works on kit-styled controls
sw-template.js network-first service worker — copy to the game, set cache name + shell list
manifest-template.json PWA manifest starter
tools/png.js dependency-free PNG encoder + shape painter for generating PWA icons
tools/sync-to-game.js vendors the kit into a game's lib/ folder
tools/stamp-version.js node lib/tools/stamp-version.js <game> --bump — bumps the -vN on the game's sw.js cache name and writes that number into index.html and version.json. Run it for every deploy instead of editing sw.js by hand
tools/contrast.js WCAG 2.x contrast measurement — --css <file> audits a game's --gk-* palette. Button colours are a contrast decision; measure them rather than eyeballing

How games consume it

Games vendor a copy (no CDN, no submodules): each game has a lib/ folder that is an exact copy of gk/ plus tools/png.js, stamped with lib/VERSION. This keeps every game self-contained (offline PWA caching keeps working) and makes kit updates deliberate — a game only changes when you re-sync it and test.

# update a game to the current kit
node tools/sync-to-game.js "D:\OneDrive\Documents\Claude Code\chicken-cross"
# then test the game, stamp a new version, commit
node lib/tools/stamp-version.js . --bump   # run inside the game directory

Wiring up a new game

Load order matters (each file extends GK):

<link rel="stylesheet" href="lib/gk-base.css" />
<script src="lib/gk-util.js"></script>
<script src="lib/gk-audio.js"></script>
<script src="lib/gk-ui.js"></script>
<script src="lib/gk-storage.js"></script>
<script src="lib/gk-profiles.js"></script>
<script src="lib/gk-pwa.js"></script>
<script src="game.js"></script>
// storage: pick a unique prefix + Firestore collection per game
const Storage = GK.createStorage({
  prefix: "mygame",
  collection: "mygame",           // add a matching rule in Firestore first!
  firebaseConfig: {...},          // same wordvoyage-e5a5c project, or null = offline-only
  blankProgress: () => ({ coins: 0, best: 0, levels: {}, updated: 0 }),
  mergeProgress: (a, b) => ({     // cross-device merge: keep the best of both
    coins: Math.max(a.coins||0, b.coins||0),
    best:  Math.max(a.best||0,  b.best||0),
    levels: /* per-level best */ {...},
  }),
});

// profiles + PIN + leaderboard
GK.Profiles.init({
  storage: Storage,
  avatars: ["🦊","🐼","🦄","🐸"],
  meta: (p, prog) => `🏁 best ${prog.best}`,
  onEnter: (p) => showMainMenu(p),
});
GK.Profiles.renderList();         // whenever the roster screen shows

// sounds: extend the defaults
Object.assign(GK.Sfx, { hop() { GK.Sfx.tone({ freq: 600, dur: 0.06 }); } });

// PWA (needs a #btn-install button, sw.js, manifest.json, icons/)
GK.initPWA({ appName: "My Game" });

// background sync; game is playable immediately
Storage.initFirebase().then(ok => badge.textContent = ok ? "☁️ synced" : "📴 offline");

New-game checklist:

  1. Copy demo/ as a starting skeleton, or wire up as above
  2. node tools/sync-to-game.js <game> to vendor lib/
  3. Copy sw-template.js → sw.js (set cache name, ending in -v1, + shell), manifest-template.json → manifest.json, then node lib/tools/stamp-version.js <game> to show the version on the home screen
  4. Write an icon script against lib/tools/png.js (see the games' tools/make-icons.js)
  5. Add a Firestore rule for the new collection in the console

Firestore data model (per game collection)

  • profile_<id> — { id, name, avatar, pin, created, updated }
  • progress_<id> — game-defined shape; must contain updated
  • deleted_<id> — tombstone so removed profiles can't be resurrected by stale devices

Auth

gk-storage.js signs in anonymously on boot, before touching Firestore, and the rules require request.auth != null. The Firebase config is public in every page load, so open rules meant anyone who viewed source could read or wipe the family's progress. The anonymous uid is per-device and identifies nobody — it is a gate against drive-by scripts, not an account, and players see no sign-in.

Sign-in failure is deliberately not fatal: if the Anonymous provider is ever turned off, games log a warning and carry on, and it is the rules that decide whether they can still sync.

The intended rules live in firestore.rules. That file is documentation, not deployment — apply it in the Firebase console (Firestore Database → Rules) or via firebase deploy --only firestore:rules.

Rollout order matters. Applying the rules before every game ships a build with anonymous auth will silently drop those games to localStorage-only:

  1. Enable Authentication → Sign-in method → Anonymous in the console.
  2. Ship gamekit ≥ 1.3.0 to every game (re-vendor, deploy).
  3. Only then apply the rules.

Tests

Data-safety tests for the persistence/sync core (gk-storage.js) — the code whose bugs would silently corrupt the kids' saved progress. No framework, just Node's built-in runner:

cd gamekit && node --test        # or: npm test

tests/storage.test.js loads the real gk-* scripts in a vm sandbox with a fake localStorage and a mock Firestore, and covers: tombstoned deletes (a removed profile can't be resurrected by a stale device or a queued push), the cross-device progress merge (keeps the best of each field, never drops an unknown key), the newer-profile-wins rule, and the debounced write path (saves coalesce and flush). The mergeProgress contract test also pins the "preserve unknown keys" rule and the score-season (sver) reset pattern.

Debug mode

Open any game with ?debug=1 for the dev panel (gk-debug.js): FPS, plus whatever toggles, actions and level-jumps that game registers. A normal player URL has no query string, so none of it runs.

Debug mode never writes progress. Jumping levels or switching on invincibility would otherwise persist, and because family sync merges by MAX an inflated score is permanent on every device. Debug is an inspection mode — look at anything, change nothing. Pass the storage object so the kit can enforce it:

GK.Debug.init({ storage: Storage });
GK.Debug.toggle("hitboxes", "hitboxes");
GK.Debug.jump("level", LEVELS.length, (n) => App.startLevel(n - 1));
// game loop:  GK.Debug.frame(dt);
// renderer:   if (GK.Debug.flag("hitboxes")) drawBoxes();

The games have no build step, so their tests load plain browser scripts via tools/test-harness.js (loadScripts — vendored into each game at lib/tools/test-harness.js). See docs/testing.md for the harness, the data-linter pattern (including reachability BFS), and the headless balance-bot pattern — the checks that catch "shipped but unfinishable / not fun" before a game reaches the kids.

Conventions the kit assumes

  • Screens: <div class="screen" id="screen-NAME">, one .active
  • Toast: <div class="toast" id="toast">
  • Profile roster container: <div id="profile-list"> (id configurable)
  • Install button: <button id="btn-install" style="display:none">
  • Admin PIN default 7777 (configurable via GK.Profiles.init({ adminPin }))
  • el.hidden = true genuinely hides — including .btn, .screen and the rest. Class rules like .btn { display:inline-flex } sit in the author origin and so beat the browser's UA [hidden] { display:none }; without the kit's !important override a hidden button stays on screen and stays clickable. Aquamarine's Roll button gave a free reroll that way.

About

Shared components for the family games: profiles, PINs, Firestore sync, WebAudio synth, PWA kit.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages