Spino Ghost Racing: race other players' ghosts, build your own tracks and run championships in a 2D top-down racing game. Entering the Best Game Award, the RevenueCat Design Award, the Keep Them Coming Back Award and the #BuildInPublic Award.

Inspiration

Spino Ghost Racing started as a 2D racer on Reddit. A community of racers grew around it, and they kept asking for more, including a real app. A game inside a web feed had its limits, so I rebuilt it from scratch as a native mobile game, first commit on August 18.

What it does

The game allows players to race each other ghost replays, make tracks and run championships.

Features:

  • Race other players' ghosts on any track
  • Race in multi-track championships
  • Build your own tracks
  • Trace a doodle and turn it into a track
  • Run your own championships (they go live after a review)
  • Doomscroll the feed of tracks and championships
  • Get notified when someone beats your time, with your new place
  • Fair on every device: replays are deterministic, so a track drives the same anywhere
  • Runs smoothly even on a 2015 phone
  • Buy car colors and chassis
  • Buy ads on the track: billboards beside the road and text on the track surface
  • Play with touch, keyboard or a controller
  • Lots of settings: personal best ghost toggle, how many ghosts, on-screen controls position, engine sound and more
  • Android
  • iOS (in App Store review-land at the time of writing)

How we built it

I gave an AI coding agent the previous project and went from there. At first it looked like a lot could be brought over, but in the end very little could move from the web version to Godot, so a complete rewrite was unavoidable. I chose Godot 4.7 because it's lightweight, powerful, free and flexible, and I've used it before.

A lot of time went into making the replays deterministic, so the server can replay every track and check it before a time goes on the board. The simulation is written in Rust: the same code runs in the game and, compiled to WebAssembly, on the server (Supabase Edge Functions), so nobody can fake a time. Rust also gave a nice performance boost: it runs beautifully on my 2015 phone.

Purchases go through RevenueCat on both stores: the server records each purchase and puts the player's billboard or road text up on the track. Push notifications go through OneSignal: when someone passes your time, the server tells you, with your new place. Along the way I wrote a OneSignal bridge for Godot (Android and iOS) and let others save some tokens: https://github.com/ksvslk/godot-onesignal

Challenges we ran into

Responsive design in Godot took a lot of time. Adding new UI elements was a struggle. I've done it myself in the past too, and it's not as straightforward as on the web. AI agents probably don't have much training data on it either.

The feed was also a challenge. Every page in the feed is a live track with a car driving on it, not a picture. At first, swiping fast froze the game and scrolling stuttered. It took a lot of fixes to make it smooth: pages now get ready in the background, and the live track takes a break whenever a menu covers it. The feed also updates while you scroll, so the page you're on must never jump to another track. This still needs work.

If you think that you will make a racing game (even a 2D one) and it just runs 60 FPS on a 2015 phone, then think again. The physics and replays are written in Rust, which is much faster, and then everything that slowed the old phone down had to go: I measured every frame on the phone itself and cut the work in half, made the scenery lighter, and stopped redrawing things that didn't change.

And when I say "I", I mean "me and my AI agents".

Accomplishments that we're proud of

  • I did not give up on getting the FPS up, and the performance is where I wanted it: smooth, even on my 2015 phone.
  • Track editor with tracing: 32 tracks on the feed already, and it's really fun to see what players make.
  • Doomscrolling (but not toxic 😍)… through race tracks?!
  • Onboarding is just a few taps.
  • No ad network, no pop-ups and no paywall: you can just play.
  • Every lap is checked by the server before it counts, so nobody can fake a time.
  • From the first commit (August 18) to live on Google Play (September 24) in 37 days.
  • I got a very warm welcome from the community: all 10 ratings on Google Play are five stars, and 6 of them came with a written review. So I must have done something right!

What we learned

Players appreciate when you listen to them.

What's next for Spino Ghost Racing

Steam! Better support for big screens.

Built With

Share this project:

Updates

Submission history