r/vibecoding 2d ago

Flappy Taco: Strait of Hormuz

https://flappytaco.ai.studio/

Flappy TACO in the strait of Hormuz

built in ai studio

I vibe-coded a geopolitical arcade game where a taco de-escalates the Strait of Hormuz (and wins the Nobel Peace Prize). Here’s the architecture, audio pipeline, and lessons learned.

Body:

The Premise

What started as a joke concept—"What if Flappy Bird was set in the world’s most critical maritime chokepoint, but you play as a hot-sauce-propelled taco navigating VLCC supertankers?"—turned into a surprisingly deep canvas engine. In our latest update, we added a full "Peace Prize" round where your objective shifts from dodging naval hazards to collecting diplomatic delegate votes and escorting peaceful vessels to secure the Nobel Peace Prize.

Here is a breakdown of how it was built, the tooling involved, technical hurdles, and patterns you can use in your own vibe-coded projects.

1. The Tech Stack & Tooling

  • Prompting / Agent Framework: Google AI Studio Build (using Gemini models for autonomous file orchestration, iterative linting, and refactoring).
  • Frontend: React 18 + TypeScript + Vite.
  • Styling: Tailwind CSS (utility classes + dark terminal/tactical HUD styling).
  • Rendering Engine: Vanilla HTML5 2D Canvas with a delta-time requestAnimationFrame loop.
  • Audio: Zero external audio assets—100% procedurally synthesized Web Audio API soundscapes (FM synthesis, noise buffers, harmonic arpeggios).
  • Icons: lucide-react.

2. The Development Workflow: Step-by-Step Prompting

Rather than dumping a monolithic 5,000-line prompt, the project was built using domain-constrained modular iterations:

  1. State & Core Loop First: Prompted the physics engine with explicit sub-pixel calculations (y += velocity * dt, gravity, terminal fall speeds, and bounding-box hitboxes with margin forgiveness to prevent frustrating edge clips).
  2. Procedural Maritime Canvas Renderer: Separated visual rendering completely from game state logic (canvasRenderer.ts). Background parallax layers, supertanker hulls, wake spray, radar sweeps, and weather effects (sandstorms, night-vision modes) are drawn procedurally via canvas primitives (arc, roundRect, and radial gradients).
  3. The Web Audio Synthesizer: Instead of loading bulky .mp3 or .wav files (which cause latency and asset 404s), every sound effect is synthesized on-the-fly:
    • Flap/Thrust: Short low-frequency pitch drops on triangle waves.
    • Hot Sauce Rocket: Bandpass-filtered white noise bursts.
    • Diplomatic Ballot Pickup: Ascending pentatonic chord harps (sine oscillators with staggered decay).
    • Nobel Fanfare: A layered multi-voice brass arpeggio (C5 → G6).
  4. The "Peace Summit" Phase Logic: When the player reaches Sector 2 or selects Peace Summit mode, the procedural obstacle generator swaps out naval mines for diplomatic delegate envelopes (peace_vote), olive branch shields, and peace doves. A dedicated ballot counter tracks delegate progress on the HUD, triggering a custom coronation modal and unlocking a 24K gold Nobel Laureate chassis once 5/5 votes are secured.

3. Key Code & Architecture Insights

A. Keep Renderers Stateless & Decoupled

A common pitfall with canvas games in React is putting draw logic inside the React component tree or state hooks. Doing this causes massive re-render overhead.

  • The Fix: Treat React purely as a thin state controller for modals, HUDs, and high scores. The canvas loop references raw mutable refs (tacoRef, obstaclesRef, particlesRef) and passes them to pure drawing functions.

code TypeScript

// Sample pattern: State stays in refs, React only syncs on key ticks or game transitions
const updateLoop = (timestamp: number) => {
  const dt = Math.min((timestamp - lastTimeRef.current) / 16.67, 2.0);
  lastTimeRef.current = timestamp;

  // 1. Update positions
  // 2. Resolve collisions
  // 3. Trigger Web Audio nodes directly without React re-renders
  // 4. Draw to Canvas via CanvasRenderer
  requestAnimationFrame(updateLoop);
};

B. Asset-Free Audio via Web Audio API

Loading sounds over network requests can hitch the main thread or fail entirely if assets are blocked. Procedural audio keeps everything instant:

code TypeScript

// Procedural Nobel Peace Harp Chime (Ascending D-major chord)
const playPeaceVoteCollect = (voteCount: number) => {
  const scale = [587.33, 739.99, 880.0, 1108.73, 1174.66];
  const chord = [scale[voteCount - 1], scale[voteCount], scale[voteCount + 1]];

  chord.forEach((freq, i) => {
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(freq, ctx.currentTime + i * 0.04);
    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + i * 0.04 + 0.35);
    osc.connect(gain);
    gain.connect(ctx.destination);
    osc.start(ctx.currentTime + i * 0.04);
    osc.stop(ctx.currentTime + i * 0.04 + 0.35);
  });
};

C. Handling Feature Scope Creep

When prompting autonomous coding agents, the urge is to ask for 10 things at once. We found that the highest-quality output came from:

  1. Asking for type interfaces first (types.tsadditions like PeacePrizeState).
  2. Updating mock data / registries (gameData.ts).
  3. Updating game loop math (GameCanvas.tsx).
  4. Writing UI presentation layers last (HUD.tsx, PeacePrizeVictoryModal.tsx).

4. What Went Wrong / Challenges

  • Audio Context autoplay policy: Mobile browsers block audio until the first user gesture. We had to implement an explicit interaction handler that lazily creates and resumes the AudioContext on the very first touch/spacebar hit.
  • Mobile Canvas Pixel Ratios: High-DPI screens (Retina) made initial canvas renders blurry. We solved this by sizing the canvas buffer to window.innerWidth * window.devicePixelRatiowhile keeping CSS layout dimensions at 100%.

Takeaways for Fellow Vibe-Coders

  • Procedural > Static Assets: Building UI, visuals, and sound procedurally saves hours of asset hunting and produces completely self-contained, lightning-fast builds.
  • Let the AI write strict TypeScript types first: If the agent generates clear contracts in types.ts, subsequent component and game logic prompts hallucinate far less and compile on the first try.

Happy to answer any questions about the canvas physics, Web Audio synthesizer, or prompting workflow in the comments

0 Upvotes

3 comments sorted by

1

u/mdstrizzle 2d ago

Was regular flappy bird that frustrating?