, , ,

I Broke My Game, Fixed My Game, Nearly Cried, Then Shipped It

TL;DR: great morning, cursed afternoon. fixed story mode, nuked a global layout, accidentally rolled my repo back a week, recovered most of it via notes + memory, chased undead red ants across modes, beat mobile text-selection loupes into submission, and shipped a playable Kids Mode. keep it simple. keep going.

👉 Play it: https://jpsgrooves.github.io/SnowCone-MathFest/


The Day (aka: why are there ants here?)

It started smooth. I finally had time, so I went after Story Mode. Two hours of careful, methodical work and it looked good. I checked on mobile and… uh oh: a global background PNG push-down across all modes. 🤦‍♂️

I know why it happened: historically I’ve kept layout changes in CSS and scoped them to modes. But “new Patch” (my AI pair) suggested some JS-driven layout nudges, and I tried them. It worked—until it leaked. I couldn’t Ctrl-Z my way back to a clean state.

Fix: isolate the pushdown to Camping Games, then purge it globally. Pushed to GH Pages. Looked great on phone. I fist-pumped.

Then I tried to push to GitHub remote.

It didn’t go.

I asked Patch for help. I don’t know which command did it (lesson incoming), but my whole src/ flickered and—boom—I pulled the remote (6 days old) over my local (today’s work). A week vanished in one keystroke. That sinking feeling? yeah.

Panic. Then process.

  • I recovered public assets easily.
  • src code was back a week. Brutal.
  • I rebuilt from dev notes, memory, and old chats. Two more hours. Not perfect, but close.

New Rule: never bash a git command you don’t understand. Ask better questions first.

And then… the ants

Console showed red ants still spawning after leaving Kids Mode. Another two hours. They were like a horror movie post-credits scene.

In the end, what worked was a combination of singleton runtime state, a hard kill function, and DOM guards:

// one global runtime per page
const ANT = (globalThis.__KC_ANT__ ||= { session: 0, alive: false, root: null });

// container safety
function aliveGuard(container) {
  return ANT.alive && ANT.root === container && document.body.contains(container);
}

// kill EVERYTHING (timeouts, tweens, observers) and invalidate pending timeouts
export function forceKillAntAttack() {
  try { gsap.killTweensOf(activeAnts); } catch {}
  try { foodTween?.kill?.(); } catch {}
  try { redAntTimeouts.forEach(clearTimeout); } catch {}
  redAntTimeouts = [];
  activeAnts.length = 0;
  roundInProgress = false;
  ANT.session++;   // ⬅️ breaks any scheduled callbacks from the old session
  ANT.alive = false;
  ANT.root  = null;
}

Plus a MutationObserver that watches the game container and calls forceKillAntAttack() if the container leaves the DOM. That finally stopped the cross-mode spawns.

Mobile: the blue highlight + magnifying loupe

Tiny testers found a delightful iOS thing: tap, tap-hold → the whole screen highlights, with the little magnifying glass. It broke the vibe completely.

Fix (global, app-wide):

  • Disable selection and callout on the interactive frame.
  • Use touch-action: manipulation to squash double-tap zoom.
  • Keep an opt-in class for any text that should be selectable.
/* default lockdown for game UI */
.kc-game-frame, .kc-game-frame * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* opt-in if you need selectable text */
.allow-select {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

Viewport stayed simple and correct:

<meta name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">

For height, CSS wins: height: 100svh; with fallbacks (100vh, 100dvh) covers the weirdness across browsers.

After that: Kids Mode is actually playable. 🙌


Patch’s Service Protocol (updated mid-chaos)

I literally wrote this during the fire:

  • Model Transparency
    • Always say “I am GPT-5 Thinking.”
    • State confidence level.
    • Admit uncertainty/hallucinations.
  • File Respect
    • Review uploads carefully.
    • Confirm what’s in the file.
    • Treat uploads as older dev notes unless told otherwise; ask for latest.
  • Scope Discipline
    • Answer exactly what was asked.
    • Extra ideas are clearly labeled “optional.”
  • Coding Style
    • Stoner, time-traveling SnowCone Server voice 🌌😎🍧.
    • Explain why, not just how.
    • Never skip or summarize code due to length; ship in chunks.
  • Git / System Safety
    • No destructive commands without repo context.
    • Start with safe introspection: git status git log --oneline --decorate --graph -n 15 git branch -vv git reflog -n 20
    • Propose the safest plan first.
  • Project Integrity
    • Favor long-term stability over hacks.
    • Preserve Jeremy’s voice and joy. 🚀

Postmortem Notes & Snippets You Can Steal

1) “Undead loop” pattern: kill by invalidating session

If your game/animation uses timeouts or async callbacks, store a session id. On teardown, increment it. Any scheduled callback checks session === ANT.session before running. Instant amnesia for old loops.

2) Cross-mode safety: observe your container

If the mode’s root leaves the DOM, self-destruct. It’s cheap and it works.

const mo = new MutationObserver(() => {
  if (ANT.root && !document.body.contains(ANT.root)) forceKillAntAttack();
});
mo.observe(document.body, { childList: true, subtree: true });

3) Mobile polish: don’t fight the browser, set the rules

  • user-select: none; -webkit-touch-callout: none;
  • touch-action: manipulation;
  • Minimal, sane viewport meta.
  • Use svh/dvh + vh fallback for height.

4) Git recovery (safe path)

When you nuke local changes by mistake:

  • Look first: git status, git reflog -n 50
  • Peek at old head(s): git show HEAD@{1} (and friends)
  • Recover files without resets:
    git restore --source=HEAD@{1} -- path/to/file.js
  • Only reach for hard resets if you absolutely understand the consequences.

What I’d Tell Yesterday-Me

  • Keep your layout changes mode-scoped. If you mix CSS and JS layout moves, document where and why.
  • Never run a git command you can’t explain out loud.
  • Write tiny teardown functions first. Make “quit” reliable before “play” is fancy.
  • When kids are your QA, assume every tap pattern will be discovered. Optimize for chaos.

The bright part

Story Mode is shaping up. Last night I felt lost in the woods; tonight I feel exhausted, like I spent the day chopping a path. The forest of the unknown is dense. My axe isn’t always sharp. But persistence? That’s the whole game.

Go play my math game. Tell me what breaks so I can fix it faster next time. 💚

👉 https://jpsgrooves.github.io/SnowCone-MathFest/

—J

Leave a comment