Home
Posted on 2/19/2024
Tags: Programming, Games
Introducing my latest game, playable right in your web browser: CaveRibbon

CaveRibbon is a one-button game where you control a ribbon gliding through a shrinking cavern. Avoid the walls and obstacles as long as you can. Compete with yourself and your friends by sharing a replay. Submit your high scores to get into the hall of fame!

CaveRibbon can be saved to your home screen as a PWA (Progressive Web App).


This game is a tribute to Sunflat's SFCave for Palm OS. You can play the original on archive.org.

In fact, it was archive.org that stirred up my nostalgia over a year ago by publishing a collection of 565 Palm OS apps, all usable on the web thanks to a Palm OS emulator.

When I was in middle school, I would play SFCave on a Palm m130 at night when I was supposed to go to bed. My dad and I had an ongoing competition to see who could get the highest score.

This was my inspiration to add the replay and share features which let you replay the exact same round, with your previous playthrough visible as a head-to-head competitor.


Some technical details:

- I used PCEImage Editor to create the pixel font and many graphics. I created the PCEImage format and tools so I could simply store and edit graphics as ASCII art. See them here.

- This game is entirely static files. There's no server-side backend. To create replay URLs, I need to serialize the entire game state into the URL itself. This proved to be challenging because there are URL size limits in browsers and messaging apps. In my first approach, I wrote a bunch of code to serialize all of the wall, obstacle, and player positions. This proved to be too much data. I came up with a few tricks to significantly shrink the data:

  - I compress player positions lossily and then interpolate them as part of deserialization. I keep only positions at the top of a peak, bottom of a valley, and when the user is close to an obstacle. I only have to keep ~4% of the original data.

  - I use a deterministic random number generator called GameRand. As long as I reuse the random number generator's seed and don't change the code to use random numbers differently I will get the exact same wall and obstacle positions in a replay.

    - I first came across GameRand in phoboslab's game underrun (source).

    - I searched for the constant 0x49616E42 to find the origins of this function. This led me down a rabbit hole: a StackOverflow post which links to a blog post (now only available on archive.org) which links to this post from 2002 introducing a faster rand function by Stephan Schaem, who appears to be the original author.

  - I couldn't just serialize the replay as JSON. That's not nearly compact enough (every digit is stored as a character!). Instead, I packed raw bytes into a buffer and serialized that into Base64. I did some tweaks to squeeze some additional efficiency (such as replacing characters that need to be percent-encoded in URLs).

  - Replay URLs for scores of 700 or higher get a nice image preview (Open Graph image) that includes the score. I generated all of these files statically using PCEImage and a script.

- This is the first project where I've made use of an LLM to help. I used ChatGPT and it had great positive impact:

  - It helped me do some things faster, such as writing boilerplate or example code that I would have had to write myself while wading through documentation.

  - It helped me do some things I wasn't willing to do myself. I don't have time to learn the right way to get specific UI behaviors using HTML and CSS. I would have compromised the UI design to fit within my current skills. With ChatGPT's help, I was able to get exactly what I wanted much faster than having to learn how to do this on my own.

  - In general, I'm energized to work on more projects with these tools. I found the accuracy to be far above what I expected. It's more than good enough for a non-mission-critical side project.

- I'm trying out some design ideas from Bret Victor's Magic Ink paper (backup). My favorite insights:

  - Most software design should focus on information graphic design, not interaction design

  - The case study of Amazon's book listings drives that point home

  - Try using sentences to describe settings rather than have a list of toggles (illustrated with his BART widget). I use this concept for sound effects settings in CaveRibbon.

  - Instead of requiring interaction, make decisions for the user based on context. I use this idea in CaveRibbon by fading out the rest of the UI during gameplay instead of having a "full screen" button.


See also:

- Sunflat's latest games for iOS and Android