TurboShells

Real-time turtle racing simulation with genetic breeding and procedural rendering

React TypeScript Vite FastAPI SQLModel PixiJS WebSocket

The Problem

Create a full-stack web application that simulates turtle racing with genetic breeding mechanics. Turtles evolve shell patterns through Mendelian inheritance while racing in real-time. The challenge was syncing a real-time simulation (30Hz server-side) with a smooth 60Hz web frontend without overwhelming the network.

My Approach

  • Hexagonal architecture separating presentation, domain logic, and data layers
  • React + Vite frontend with PixiJS for efficient 2D rendering
  • FastAPI + SQLModel backend with WebSocket real-time communication
  • Genetic algorithm for turtle trait inheritance (shell patterns, speed)
  • SQLite persistence for race history and breeding records
  • Protocol Buffers for efficient binary serialization over WebSocket

Key Highlights

  • Fully deployable to Vercel (frontend) and Railway (backend)
  • Real-time simulation: 30Hz server physics → 60Hz client render
  • Clean hexagonal architecture (easy to extend and test)
  • Complete documentation with architecture diagrams
  • SQLite database tracks 1000+ races without slowdown

How It Works

TurboShells is my showcase project for full-stack web architecture. It’s a deceptively simple idea—turtles race and breed—implemented with production-grade patterns.

The Concept

Imagine a racing game where victory doesn’t make you fast; it makes your offspring fast. Each turtle inherits traits (shell pattern, speed, acceleration) from parents. Winners breed, creating a population that evolves toward faster racers. Watch as the web UI displays real-time evolution.

Why This Project?

TurboShells demonstrates:

  • Full-stack competence: I can build from database schema to React components
  • Real-time systems: WebSocket sync at scale without race conditions
  • Architecture thinking: Hexagonal design = code that’s easy to test and extend
  • Deployment: Everything deploys to free tiers (Vercel + Railway)

Architecture Deep Dive

Backend (FastAPI):

  • Manages turtle state and race simulation (30 ticks per second)
  • SQLModel ORM for clean database queries
  • WebSocket endpoint broadcasts race events in real-time
  • Genetic algorithm applies Mendelian inheritance rules

Frontend (React + PixiJS):

  • Renders turtles, tracks, shell patterns at 60 FPS
  • WebSocket listener updates state incrementally
  • Vite bundler keeps bundle size under 150KB
  • Responsive design works on mobile

Sync Protocol: Server sends turtle positions at 30Hz. Client interpolates to 60Hz rendering. This decoupling prevents “choppy” animation while keeping network bandwidth reasonable.

Genetic Mechanics

Each turtle has traits: shell color, shell pattern, speed, acceleration. When turtles breed, offspring inherit:

  • Dominant/recessive genes (60% Dad’s speed, 40% Mom’s)
  • Mutation chance (5% to randomly vary inherited traits)
  • Phenotype = visible shell pattern, expressed behavior

Mathematically accurate without being tedious. Players intuitively understand “fast parents = faster kids.”

Deployment

Frontend: npm run build → Deploy /dist to Vercel. Free tier, auto-redeploy on git push.

Backend: Docker image → Railway container. Free tier includes 5GB persistent storage for SQLite.

Result: Production-grade full-stack app costs $0/month to run.

Why Hexagonal Architecture?

Traditional MVC couples everything. Hexagonal (ports & adapters) separates:

  • Domain: Business logic (genetics, physics)—language agnostic
  • Adapters: React (web), FastAPI (API), SQLModel (database)
  • Ports: Abstract interfaces that adapters implement

This means I can swap out React for Vue, FastAPI for Django, without touching domain logic. Tests run 100x faster because they don’t hit the database.

Lessons Learned

This project taught me that architecture isn’t overhead—it’s acceleration. The first 10% of features took 60% of the time (architecture design). The remaining 90% took 40% (architecture pays off).

WebSocket real-time systems are deceptively hard. Naive approaches create race conditions. Proper sync requires idempotent state updates and careful ordering.

Full-stack ownership is powerful. I controlled every layer, which meant I could optimize end-to-end instead of fighting interfaces.

Explore