Sor 6 is a dice shooting game for the Playdate console, originally made for PlayJam 9.
Pictured above is a dice simulator showing 24 dice that bounce around in response to accelerometer input, but most of this game is actually nothing like that. Instead, it's about shooting dice at a dice grid, with 12 different songs and 12 different backdrops to match.
So here is a story of how I spent 7 weeks making a dice game that doesn't involve any dice rolling until 6 weeks after it was first released.
PlayJam 9 took place from February 13 through 16. This would be the 4th jam that I participated in, the first three were PlayJam 6, PlayJam 7, and PlayJam 8. These jams are great motivators for finishing games, because as we all know, it takes time to make stuff, but it takes deadlines to actually ship them.
Day 1: The theme this time was "dice". I expected many entries to use "dice" as a verb, or roll dice to drive some tabletop game. I thought I should make some effort to avoid these mechanics, although it turns out only so few entries did what I expected (there were 2 dicing action games and one tabletop game, out of 13 total). So I was already stuck on day 1 because I decided not to do the first two ideas that came to mind, and I didn't have a third idea.
I knew I at least needed dice images, so that's what I did on the first day. Instead of drawing all the dice images manually, I wrote a generator that generated dice images in SVGs and built some scripting to rasterize them. This turns out to be a good implementation decision that served me well throughout this project:
Day 2: Having gotten my dice images, I spent the second day implementing the code to render them, which is simply drawing bitmaps from a large image table indexed by (rx,ry,rz,explode) values. This comes out to 16*16*16*5 = 20480 images at 48x48 pixels each, which eats up ~4MB of space and ~10 seconds of loading time. It's not great but it's working, so I figured I will deal with it later.
Second day was also when I integrated a music player. Having seen dice rolling around and having been listening to Fernando Sor's opus 6 no 11, I thought my game was going to be exactly that -- dice that roll around to the tune of Sor's song. There is a tenuous connection between a 6-sided die and the song I selected being from Sor's opus 6, but the reality is this was just the third random idea that came to mind. Thus I named the project "Sor6", I figured I will add all the remaining songs from opus 6 after the jam is over. As a side note, every entry in this jam used a 6-sided die, as opposed to something like a D20. Maybe I should make a D20 version with Segovia's 20 Sor studies.
The music data was encoded in a bespoke format that resembles guitar tabs. This gives me the ability to edit all songs as plain text files. I suspect most people would use purpose built song tracking software and then just play MIDI files at run time, but there are some things I wanted to do with the song data, and I didn't want to deal with parsing MIDI. How I handled the tab files:
Day 3: I spent the third day hooking everything together to make a playable game, mostly working on grid collisions and game state transitions. It was a bit disconcerting that I didn't have anything playable until the third day, but I was fairly confident that I could finish my completely unambitious game, and I did. The game was not much more than dice rolling on a blank background hitting other dice, but it was stable and didn't have any unexpected behavior.
Day 4: Last day was spent on polishing, including tweaking UI text and adding launcher graphics. I also started working on the background that goes behind the dice grid, but couldn't finish it in time, so I disabled the broken background feature and submitted the final entry with a blank background. The jam page says I made it 19 minutes and 22 seconds before the deadline. I knew the game was incomplete, but at least it was playable without bugs. I thought it seemed OK for 3 days worth of work.
I had two goals for the full game, one is to add all the remaining songs from Opus 6, and the other was to add background graphics to all songs. I figured I would be done in 2 weeks, but ended up taking 7 weeks.
Week 1: First week was spent on fixing obvious annoyances in the jam version. One obvious inefficiency was in the large image table size. Since the different dice faces aren't really readable when exploded, I split the dice image table into two, one with (rx,ry,rz) images for the main dice images (at 38x38 pixels), and the other with (rx/4,ry/4,rz/4,explode) images for the explosions (at 48x48 pixels). These came out significantly smaller, so much so that I added another dice variation and more explosion frames, and the total still came out to around ~1MB. This is the main reason why the full version ZIP is ~3MB smaller than the jam version, despite having more content and loading faster.
First week was also when I added respawn capability to the dice grid. In the jam version, the exploded grid objects are never replaced, so the population becomes noticeably sparse near the end of the game. It's noticeably sparse despite the initial population being calculated to fit the duration of song 11, so it wasn't going to work for the much longer song 12. After the first week, the dice grid rarely gets sparse.
Week 2: Second week was when I transcribed all remaining 11 songs from opus 6, so that the game can live up to the short "Sor 6" title. Finding scores for classical songs is not difficult, the problem is actually finding too many scores for the same song. This turns to be a problem in week 5.
One immediate concern was the lack of ability to select different songs, and that was promptly addressed by adding a title screen menu, a game over menu, and some in-game controls to skip between songs.
Next problem was due to song 3, which was in allegro timing with 148 notes that were 70 milliseconds apart on the same string. Jam version only had song 11, which was in allegro moderato timing with only 2 notes that were 200 milliseconds apart on the same string, so I could avoid some clipping noise caused by playing successive notes on the same channel by turning the notes off slightly early. 70 milliseconds is too short a duration to cut, so the new fix was to assign two channels for each guitar string, and play successive notes on alternate channels. Everything instantly sounded better, it's like how I felt when I first heard songs that made use of Impulse Tracker's New Note Action feature after listening to all the MOD files that didn't have it.
Weeks 3-4: I still haven't worked on the backgrounds yet, but I got this idea that the songs mostly play the same. The notes and timings are different, which does have an effect in the rate which the projectiles are launched, but the game mostly play the same regardless of which song you choose. I thought the fix for this was to add a bonus target that player needs to find for each song. So, I added Bocchi-chan. Bocchi-chan can go anywhere! This is the 4th appearance of Bocchi-chan in my Playdate games.
The path she follows is generated in song-specific ways. For each song, there is an initial direction that player needs to go to intercept Bocchi-chan, but after the initial deterministic period, each song will have Bocchi-chan go on procedurally generated paths that are unique for each run. The only exception is song 11, which is fully deterministic. Example for song 7 is below, where every quick turn corresponds to an arpeggio in the song.
Week 5: I spent weeks 3-4 writing generators for the song specific paths, but around end of week 4 was when I started worrying about the songs themselves. Recall from week 2 that there are usually multiple scores for each song, and as it turns out, those versions do not agree with each other. It's not the case that I would be happy if I simply pretend that the first version I found would be canonical, because the reason why I noticed was because some songs sounded weird. Week 5 was when I wrote a script to plot the tab data back as scores so that I can verify them more easily. I would spend the next week and a half verifying each song against the multiple copies I found.
This was an exceedingly tedious process, especially considering that many files found online are obvious badly transcribed copies themselves. But by the end of week 5, I am fairly confident that the songs are at least consistent against some existing version of the scores, without any transcription errors on my part. Week 5 was also when I added a debug backdoor with song test, because it's really hard to debug songs without a rewind function, and it was not possible to rewind game time since it would break the special target path generation.
Week 6-7: Having add a debug song test, I thought maybe I should add a debug menu to test the dice sprites as well, otherwise all my generated dice images are mostly wasted (players mostly see only 48 out of the 4096 images in the dice grid). But rather than doing that, I thought I would implement a full dice simulator. It took a bit of code to fake physics but the end result worked well. So at week 6, this dice game finally allowed players to roll dice.
Recall I had two goals for the full version: one was to collect all the songs from opus 6, which is done by end of week 5. The other is to add background to all the songs, and the final two weeks was when I worked on the backgrounds. I mostly had some image in my head for each song when I implemented the special target paths, and the backgrounds were implemented to match those paths. Summary for the backgrounds are below, listed in the order which they were implemented:
The completion of the background for song 11 marks the completion of this game. I am especially proud of how songs 9 and 11 turned out.
I announced the full version to the usual places:
I got a few downloads, better than Xor Constellation at least (not a particularly high bar).
It had been a fun 7 weeks working on this project, I would say it was time well spent.
Previous (2025-12-05): HG GQuuuuuuX