<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Get Info: #yoyozo</title>
    <description>Posts tagged “yoyozo” — Blog of independent game and app developer Matt Sephton. Featuring vintage Macintosh, game development, digital artwork, Japanese esoterica, video game reviews, hacks and tips, and much more.</description>
    <link>https://blog.gingerbeardman.com/tag/yoyozo/</link>
    <atom:link href="https://blog.gingerbeardman.com/tag/yoyozo/index.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 15 Jul 2026 17:33:15 +0000</pubDate>
    <lastBuildDate>Wed, 15 Jul 2026 17:33:15 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>

    
      
        <item>
          <title>A year in the life of a Playdate game developer</title>
          <description>&lt;p&gt;This week sees the &lt;a href=&quot;https://play.date/games/tags/on-sale&quot;&gt;first Anniversary Sale of Catalog, the official store for Playdate games&lt;/a&gt;. This means it’s almost the anniversary of &lt;a href=&quot;/2023/04/13/sparrow-solitaire-for-playdate/&quot;&gt;the release Sparrow Solitaire&lt;/a&gt;, which was the first Playdate work I did in 2023 after taking an enforced break.&lt;/p&gt;

&lt;p&gt;So I thought it would be interesting to look back and take stock of the last year of me making stuff for Playdate. What did I finish? What did I abandon? What is still to come?&lt;/p&gt;

&lt;p&gt;After Sparrow Solitaire (a huge game that took around a year of development time split between myself and Mac Vogelsang), it took me a few months to figure out the right sort of size and scope that would allow me to make a game every month or two, whilst minimising the risk of not selling enough. By September I was well and truly &lt;em&gt;in the zone&lt;/em&gt; and &lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;created YOYOZO&lt;/a&gt; which went on to receive a commendation from Ars Technica as one of the &lt;a href=&quot;https://arstechnica.com/gaming/2023/12/ars-technicas-best-video-games-of-2023/7&quot;&gt;“Best Games of 2023”&lt;/a&gt;. And at the &lt;a href=&quot;https://play.date/games/community-awards-2023/&quot;&gt;2023 Playdate Community Awards&lt;/a&gt;, presented on 8th March 2024, Sparrow Solitaire won &lt;em&gt;Best Puzzle Game&lt;/em&gt; and YOYOZO won &lt;em&gt;Best Arcade Game&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Somebody asked me if this is my full-time job? My answer is: yes, this is my only job and I do it as often as I’m able. It’s certainly more complicated, due to various life stuff, but that’s the gist of things. Over the last twelve months, I would say I’ve been productive for around half my time.&lt;/p&gt;

&lt;p&gt;Recently I saw somebody say &lt;a href=&quot;https://twitter.com/indieretropod/status/1764443576597946593&quot;&gt;“Be careful with scope and look what you can do in a year!”&lt;/a&gt;, about the wonderful &lt;a href=&quot;https://twitter.com/johanpeitz&quot;&gt;Johan Peitz&lt;/a&gt;, and I think that addage can also be applied to my output this year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; 22 projects, 10 of which were released, and 5 are still to come. I’m pretty happy with that!&lt;/p&gt;

&lt;p&gt;I also managed to squeeze in a couple of non-Playdate projects: my first real Love2D game &lt;a href=&quot;https://gingerbeardman.itch.io/portrait-curling&quot;&gt;Portrait Curling&lt;/a&gt;, modifications to vintage pixel art app &lt;a href=&quot;/2024/01/22/jinzo-paint-vintage-mobile-drawing-app/&quot;&gt;JINZO Paint&lt;/a&gt;, and I’m always &lt;a href=&quot;/&quot;&gt;blogging&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don’t forget to &lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;check out my Playdate games in the Catalog Sale!&lt;/a&gt; And my &lt;a href=&quot;/tag/playdate/&quot;&gt;other Playdate blog posts&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Month&lt;/th&gt;
        &lt;th&gt;Project&lt;/th&gt;
        &lt;th&gt;Genre&lt;/th&gt;
        &lt;th&gt;Status&lt;/th&gt;
        &lt;th&gt;Store&lt;/th&gt;
        &lt;th&gt;Blog&lt;/th&gt;
        &lt;th&gt;Made With&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-03&lt;/td&gt;
        &lt;td&gt;Sparrow Solitaire&lt;/td&gt;
        &lt;td&gt;Puzzle&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/sparrow-solitaire/&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/sparrow-solitaire/&quot;&gt;Catalog&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;/2023/04/13/sparrow-solitaire-for-playdate/&quot;&gt;Read&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://twitter.com/Vogelscript&quot;&gt;vogelscript&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-04&lt;/td&gt;
        &lt;td&gt;Shark Turtle&lt;/td&gt;
        &lt;td&gt;Puzzle&lt;/td&gt;
        &lt;td&gt;Unreleased&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-05&lt;/td&gt;
        &lt;td&gt;Radical&lt;/td&gt;
        &lt;td&gt;Adventure&lt;/td&gt;
        &lt;td&gt;Abandoned&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-05&lt;/td&gt;
        &lt;td&gt;Carton&lt;/td&gt;
        &lt;td&gt;Puzzle&lt;/td&gt;
        &lt;td&gt;On hold&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-06&lt;/td&gt;
        &lt;td&gt;Heno Heno&lt;/td&gt;
        &lt;td&gt;Puzzle&lt;/td&gt;
        &lt;td&gt;On hold&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-06&lt;/td&gt;
        &lt;td&gt;Fore! Track&lt;/td&gt;
        &lt;td&gt;Sports&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/fore-track/&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/fore-track/&quot;&gt;Catalog&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;/2023/06/26/ball-und-panzer-golf-making-a-playdate-game-in-a-week/&quot;&gt;Read&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-07&lt;/td&gt;
        &lt;td&gt;Mandala&lt;/td&gt;
        &lt;td&gt;Toy&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/gingerbeardman/mandala&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;GitHub&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-08&lt;/td&gt;
        &lt;td&gt;Strategies&lt;/td&gt;
        &lt;td&gt;App&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://gingerbeardman.itch.io/strategies&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Itch&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-08&lt;/td&gt;
        &lt;td&gt;DateDiff&lt;/td&gt;
        &lt;td&gt;App&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://gingerbeardman.itch.io/datediff&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Itch&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-08&lt;/td&gt;
        &lt;td&gt;Fortress&lt;/td&gt;
        &lt;td&gt;Puzzle&lt;/td&gt;
        &lt;td&gt;Abandoned&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-08&lt;/td&gt;
        &lt;td&gt;Mole Hole&lt;/td&gt;
        &lt;td&gt;Adventure&lt;/td&gt;
        &lt;td&gt;Unreleased&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://twitter.com/thoruman&quot;&gt;thoruman&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-09&lt;/td&gt;
        &lt;td&gt;Rink&lt;/td&gt;
        &lt;td&gt;Sports&lt;/td&gt;
        &lt;td&gt;Tutorial&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-09&lt;/td&gt;
        &lt;td&gt;Kye&lt;/td&gt;
        &lt;td&gt;Puzzle&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://mouflon-cloud.itch.io/kye&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Itch&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://twitter.com/MouflonCloud&quot;&gt;mouflon cloud&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-09&lt;/td&gt;
        &lt;td&gt;YOYOZO&lt;/td&gt;
        &lt;td&gt;Action&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;Catalog&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;Read&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-10&lt;/td&gt;
        &lt;td&gt;Pararena&lt;/td&gt;
        &lt;td&gt;Sports&lt;/td&gt;
        &lt;td&gt;Abandoned&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-10&lt;/td&gt;
        &lt;td&gt;Soccer&lt;/td&gt;
        &lt;td&gt;Sports&lt;/td&gt;
        &lt;td&gt;Prototype&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-10&lt;/td&gt;
        &lt;td&gt;Super ICARUS&lt;/td&gt;
        &lt;td&gt;Action&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/icarus/&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://play.date/games/icarus/&quot;&gt;Catalog&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-11&lt;/td&gt;
        &lt;td&gt;Boing&lt;/td&gt;
        &lt;td&gt;Action&lt;/td&gt;
        &lt;td&gt;Prototype&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-11&lt;/td&gt;
        &lt;td&gt;Cranxious&lt;/td&gt;
        &lt;td&gt;Action&lt;/td&gt;
        &lt;td&gt;Prototype&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2023-12&lt;/td&gt;
        &lt;td&gt;See The Sky&lt;/td&gt;
        &lt;td&gt;Storybook&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://gingerbeardman.itch.io/see-the-sky&quot;&gt;Released&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Itch&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;/2023/12/16/see-the-sky-thoru-yamamoto-christmas-story-for-playdate/&quot;&gt;Read&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;&lt;a href=&quot;https://twitter.com/thoruman&quot;&gt;thoruman&lt;/a&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2024-01&lt;/td&gt;
        &lt;td&gt;Rocket Man&lt;/td&gt;
        &lt;td&gt;Simulation&lt;/td&gt;
        &lt;td&gt;On hold&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2024-02&lt;/td&gt;
        &lt;td&gt;Eggdog&lt;/td&gt;
        &lt;td&gt;Action&lt;/td&gt;
        &lt;td&gt;Prototype&lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
        &lt;td&gt; &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 07 Mar 2024 19:44:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/03/07/a-year-in-the-life-of-a-playdate-game-developer/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/03/07/a-year-in-the-life-of-a-playdate-game-developer/</guid>
        </item>
      
    
      
        <item>
          <title>Dynamic music and sound techniques for video games</title>
          <description>&lt;p&gt;The only aspect of game development I’ve not attempted myself is the music. I mostly use royalty free music of Japanese origin (just because I dig their vibe, man) as in the case of &lt;a href=&quot;https://soundcloud.com/mac-vogelsang/sets/sparrow-solitaire&quot;&gt;&lt;em&gt;Sparrow Solitaire&lt;/em&gt;&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1732555533863751691&quot;&gt;&lt;em&gt;Fore! Track&lt;/em&gt;&lt;/a&gt; or in rare cases I pay friends (like the amazing Jamie Hamshere) to write music specifically for a game as in the case of &lt;a href=&quot;https://soundcloud.com/gingerbeardman/sets/yoyozo-soundtrack&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt;&lt;/a&gt;. Maybe one day that will change, but until then I’m enjoying gaining more understanding and control of the music in my games. Whilst I develop games for &lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; these techniques are general enough to apply anywhere.&lt;/p&gt;

&lt;p&gt;The main way I make the music into more than a static track is to apply a dynamic, reactive, or adaptive effect in one way or another. In this blog post I’ll go into how I’ve achieved this. Please note this is by no means an exhaustive list, rather it’s just the ones I have personally used.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;dynamic-bpm&quot;&gt;Dynamic BPM&lt;/h2&gt;

&lt;p&gt;I use this method in &lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt;&lt;/a&gt; because it uses “chip tune” music data representing songs composed by my friend Jamie Hamshere using &lt;a href=&quot;https://play.date/pulp/&quot;&gt;&lt;em&gt;Playdate Pulp&lt;/em&gt;&lt;/a&gt;. A playback engine for this data, written by Pulp creator Shaun Inman, works beautifully when integrated into games written using Lua and the &lt;a href=&quot;https://play.date/dev/&quot;&gt;&lt;em&gt;Playdate SDK&lt;/em&gt;&lt;/a&gt;. I added hook to allow me to set the BPM at any point to any value. The end result is that the BPM of the music scales from 130 to 135 as your score increases. As you improve at the game you’ll notice the music speed up ever so slightly along with an increase in tension and anxiety.&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;140&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1685873466&amp;amp;color=%23ff5500&amp;amp;auto_play=false&amp;amp;hide_related=false&amp;amp;show_comments=true&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=true&amp;amp;visual=true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Of course, it’s possible to do this in a pre-recorded song stored as a digital music file, but it’s much more difficult for that to respond to the what the player does in the game. An example that takes an interesting approach to this is the track &lt;a href=&quot;https://www.youtube.com/watch?v=1_iZh_2li4M&quot;&gt;“Sunny Day” from the game Vib Ribbon&lt;/a&gt;, and indeed the rest of its soundtrack, where tempo changes over the duration of each song.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;infinite-variations&quot;&gt;Infinite Variations&lt;/h2&gt;

&lt;p&gt;Another technique I use in &lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt;&lt;/a&gt;, again made possible because I can modify the music data and playback parameters in real time. With this one I cycle the values for the instrument voices pseudo-randomly so that the track plays once as it was programmed and then morphs slightly for each subsequent playback. The track is quite minimal and repetitive in &lt;a href=&quot;https://sites.barbican.org.uk/reichglassadams/&quot;&gt;Steve Reich, Philip Glass or John Adams&lt;/a&gt; sort of way, so there are automated variations wandering around the original arrangement work really well. Perhaps the &lt;a href=&quot;https://www.youtube.com/watch?v=NkBXgcN3fXo&quot;&gt;ultimate implementation of this approach is &lt;em&gt;Wii Play’s&lt;/em&gt; Tanks game&lt;/a&gt;.&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;140&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1685873439&amp;amp;color=%23ff5500&amp;amp;auto_play=false&amp;amp;hide_related=false&amp;amp;show_comments=true&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=true&amp;amp;visual=true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;For sound effects, I vary the playback sample rate to change the pitch of sound effects. This prevents the same sound effect becoming monotonous. Two examples might be &lt;em&gt;Lara Croft&lt;/em&gt; in the first &lt;em&gt;Tomb Raider&lt;/em&gt; game, &lt;a href=&quot;https://youtu.be/Roi2UelYGsU?si=_17TmHon5JenRxCM&amp;amp;t=1079&quot;&gt;groaning the same way every time she climbs up a platform&lt;/a&gt; compared with &lt;a href=&quot;https://www.youtube.com/watch?v=JGQeQmUuMas&quot;&gt;the rich variety of sounds when &lt;em&gt;Mario&lt;/em&gt; walks on different surfaces&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;blendingfadingbalance&quot;&gt;Blending/Fading/Balance&lt;/h2&gt;

&lt;p&gt;Another idea I had was to fade or blend two tracks as the player makes progress in the game. But how to find two tracks that can be cross-faded in a way that always makes sense? Of course you can have them composed, but what about in music that already exists? If only there was an easy way to find such tracks!&lt;/p&gt;

&lt;p&gt;There is: stereo pairs! You’d be surprised at how different the left and right channels can sound whilst obviously being the same tune. Of course this means that are output audio will be mono but for me on Playdate that’s just fine. I use this method in &lt;a href=&quot;https://play.date/games/fore-track/&quot;&gt;&lt;em&gt;Fore! Track&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea is to adjust the balance of the two parts of the audio, at once point you’re playing just the left audio across both outputs, then you adjust the balance to play a mix of both, at the other end of the scale you’d be playing just the right audio. Unfortunately the Playdate SDK currently has no API to easily adjust balance, so I had to program a method myself. First, I convert to the destination format which is for me ADPCM using &lt;a href=&quot;https://github.com/dbry/adpcm-xq&quot;&gt;adpcm-xq&lt;/a&gt;. Once the files are in this format I split the stereo pair into two files, one for the left channel and one for the right channel. Converting to the destination format before splitting ensures that the two are exactly the same length in terms of samples/bytes.&lt;/p&gt;

&lt;p&gt;In the game I fade between the two as the score/chain increases, which has the effect of subtly changing the instrumentation of the tune. It’s one of those things that most people wouldn’t notice, but that once you know about it you can’t miss it. Sadly, I don’t have an easy way to demo this in a video or sound file.&lt;/p&gt;

&lt;p&gt;For sound effects, you might consider panning to increase immersion and guide the players visual focus through use of audio. In &lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt;&lt;/a&gt; I pan certain sounds relative to the location of the ball, certain other sounds relative to the location of the player, and there are global sound effects that are not panned at all.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yoyozo-teaser.gif#playdate&quot; alt=&quot;YOYOZO&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;progressive-loops&quot;&gt;Progressive Loops&lt;/h2&gt;

&lt;p&gt;Digital audio is a different beast. I always try to find a time that fits the game, going so far as to audition many hundreds of tracks and creating playlist of songs far ahead of ever making a game or even having and idea for a game. I try to find tracks that will loop well and not get annoying, which is easier said than done. If I can’t find a track that loops well, there’s another way.&lt;/p&gt;

&lt;p&gt;You can use &lt;a href=&quot;https://github.com/arkrow/PyMusicLooper&quot;&gt;PyMusicLooper&lt;/a&gt; to analyse a digital audio track and spit out information about ranges that loop nicely, along with a percentage indicating how good it considers the loop. In other words you can identify and extract a loop from digital audio files that sound like they could loop. Of course, can’t identify loops in tracks that aren’t repetitive or consistent in their structure.  You might get PyMusicLooper to split the file into into three sections (intro, loop, outro) or just export the loop information as a text file to use in your game. Which I choose depends on how much of the file I want to use.&lt;/p&gt;

&lt;p&gt;For an example, in my game &lt;a href=&quot;https://play.date/games/icarus&quot;&gt;Super ICARUS&lt;/a&gt; I’m using a file that gives the vibe I wanted in the game and sounded like it contained some loops even though it was not provided as a looping song. PyMusicLooper reported that it contains tens of possible loops of varying quality.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;loop&lt;/th&gt;
      &lt;th&gt;start&lt;/th&gt;
      &lt;th&gt;end&lt;/th&gt;
      &lt;th&gt;duration&lt;/th&gt;
      &lt;th&gt;match&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;3.437&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;30.755&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;27.318&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;94.87%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.023&lt;/td&gt;
      &lt;td&gt;27.341&lt;/td&gt;
      &lt;td&gt;27.318&lt;/td&gt;
      &lt;td&gt;94.79%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;7.279&lt;/td&gt;
      &lt;td&gt;34.598&lt;/td&gt;
      &lt;td&gt;27.319&lt;/td&gt;
      &lt;td&gt;94.63%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;6.850&lt;/td&gt;
      &lt;td&gt;34.168&lt;/td&gt;
      &lt;td&gt;27.318&lt;/td&gt;
      &lt;td&gt;93.95%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;8.127&lt;/td&gt;
      &lt;td&gt;35.445&lt;/td&gt;
      &lt;td&gt;27.318&lt;/td&gt;
      &lt;td&gt;93.92%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;22.221&lt;/td&gt;
      &lt;td&gt;52.953&lt;/td&gt;
      &lt;td&gt;30.732&lt;/td&gt;
      &lt;td&gt;93.80%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;25.635&lt;/td&gt;
      &lt;td&gt;56.366&lt;/td&gt;
      &lt;td&gt;30.731&lt;/td&gt;
      &lt;td&gt;93.23%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;11.970&lt;/td&gt;
      &lt;td&gt;39.288&lt;/td&gt;
      &lt;td&gt;27.318&lt;/td&gt;
      &lt;td&gt;93.17%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;6.850&lt;/td&gt;
      &lt;td&gt;35.875&lt;/td&gt;
      &lt;td&gt;29.025&lt;/td&gt;
      &lt;td&gt;92.13%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;9&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;6.850&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;54.660&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;47.810&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;91.98%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10&lt;/td&gt;
      &lt;td&gt;20.515&lt;/td&gt;
      &lt;td&gt;52.953&lt;/td&gt;
      &lt;td&gt;32.438&lt;/td&gt;
      &lt;td&gt;91.54%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;11&lt;/td&gt;
      &lt;td&gt;10.263&lt;/td&gt;
      &lt;td&gt;37.581&lt;/td&gt;
      &lt;td&gt;27.318&lt;/td&gt;
      &lt;td&gt;91.26%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;22.221&lt;/td&gt;
      &lt;td&gt;54.660&lt;/td&gt;
      &lt;td&gt;32.439&lt;/td&gt;
      &lt;td&gt;91.11%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;13&lt;/td&gt;
      &lt;td&gt;23.928&lt;/td&gt;
      &lt;td&gt;68.325&lt;/td&gt;
      &lt;td&gt;44.397&lt;/td&gt;
      &lt;td&gt;90.92%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;14&lt;/td&gt;
      &lt;td&gt;39.300&lt;/td&gt;
      &lt;td&gt;70.031&lt;/td&gt;
      &lt;td&gt;30.731&lt;/td&gt;
      &lt;td&gt;90.82%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;12.399&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;70.449&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;58.050&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;90.78%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;My goal was to find three loops of increasing length and with a high percentage loop quality. After some experimentation and listening, I decided on loops 0, 9, and 15 (table only shows the top 15 loops from this track, even though their percentage loop match are not 100% they still sound like good loops, so selecting these loops was a case of finding three of suitable length and content. PyMusicLooper will let you audition the loops directly, so there’s no need to use an audio editor.&lt;/p&gt;

&lt;p&gt;Using the Playdate SDK I can do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setRange()&lt;/code&gt; on the audio track to change the playback range and the music will loop between those new points when the playhead reaches the end of the range. For this reason, this method does not provide immediate results so is better used to signify a large change in progress as the delay until the change is noticed will be an unknown amount of time. But when the change does kick in it’s a really nice surprise!&lt;/p&gt;

&lt;p&gt;The final result sees the game start by playing loop 1 (synth and drums) and then as the player gets makes some good progress I switch to loop 2 (synth, drums, guitar licks), and finally as they pass a certain threshold I switch to loop 3 (synth, drums, guitar licks into guitar solo). This provides music that sounds very dynamic with little effort. You could even drop back to the shorter loops if the player lost a life, missed a target, and so on. Again, there’s no real way of me demoing this as it’s something that will become apparent through play, and the final result is just one long dynamic song!&lt;/p&gt;

&lt;p&gt;For sound effects I use the same approach as above. As an example, in &lt;a href=&quot;https://play.date/games/fore-track/&quot;&gt;&lt;em&gt;Fore! Track&lt;/em&gt;&lt;/a&gt; there is a clapping sound effect after the player gets the ball in a hole. This is a long sound effect but I play three increasingly long sections of it as the player’s chain increases (number of successive holes-in-one). It starts off as a short clap, increases to a longer more enthusiastic clap, and finally it begins with a whoop and continues to enthusiastic clap. I have a separate sound effect for the end game cheer that plays over the top of the full clap, resulting in a raucous end of game celebration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;quantised-sounds&quot;&gt;Quantised Sounds&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;https://play.date/games/icarus&quot;&gt;Super ICARUS&lt;/a&gt; created certain game event sounds from small sections of the music track. I then adjust playback rate/speed/pitch. The result is that the sounds appear to be quantised or matched to the music. I’m not sure how to describe this phenomenon accurately in musical/technical terms.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I’d love to hear about other methods of achieving dynamic music and sound in video games. Feel free to reach out to me on social media!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt; (or, how I made a Playdate game in 39KiB)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/11/26/easter-egg-emoji-converting-pixels-into-particles/&quot;&gt;Easter egg emoji: converting pixels into particles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/12/09/dynamic-music-and-sound-techniques-for-video-games/&quot;&gt;Dynamic music and sound techniques for video games&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;elsewhere&quot;&gt;Elsewhere&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;2023-11-22—&lt;a href=&quot;https://news.ycombinator.com/item?id=38584336&quot;&gt;Hacker News&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-11-22—&lt;a href=&quot;https://tildes.net/~games/1crg/dynamic_music_and_sound_techniques_for_video_games&quot;&gt;Tildes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 09 Dec 2023 01:22:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/12/09/dynamic-music-and-sound-techniques-for-video-games/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/12/09/dynamic-music-and-sound-techniques-for-video-games/</guid>
        </item>
      
    
      
        <item>
          <title>Easter egg emoji: converting pixels into particles</title>
          <description>&lt;p&gt;I’m &lt;a href=&quot;/2019/08/14/moai-games/&quot;&gt;fascinated with Moai&lt;/a&gt; so I always try to squeeze an appearance into my games. Moai in video games is a meme, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Easter_egg_(media)&quot;&gt;easter egg&lt;/a&gt;, going all the way back to 1983. But my game YOYOZO (&lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;out now for the Playdate handheld&lt;/a&gt;) is about capturing stars in space using a yoyo, so how could I get a Moai in it?&lt;/p&gt;

&lt;p&gt;The source of my inspiration was a trip to Japan back in 2004 (my only one, so far). Visiting in August meant that one of the things we did was go to an &lt;a href=&quot;https://www.japan-guide.com/e/e2267.html&quot;&gt;annual hanabi fireworks festival&lt;/a&gt;, where I saw &lt;a href=&quot;https://blog.gaijinpot.com/four-types-of-japanese-fireworks/&quot;&gt;katamono&lt;/a&gt; for the first time. These are fireworks that explode in the shapes of drawings, like a smiley face or a magic 8-ball. I was amazed and the experience has stuck with me for over 20 years. Maybe I could add Moai into YOYOZO by making the explosions appear like the katamono?&lt;/p&gt;

&lt;p&gt;I started off by coding the patterns by hand, as a test, but my calculations weren’t precise enough and the whole endeavour quickly grew too complicated to manage by hand. I needed a better, more automated way. The method that I arrived at is what I’ll document in this post.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;pixels-as-code&quot;&gt;Pixels as code&lt;/h2&gt;

&lt;p&gt;Instead of plotting values by hand in code I figured that it would make more sense if I could draw the patterns and then somehow convert them into coordinates. I use &lt;a href=&quot;/2023/05/10/piskel-for-playdate/&quot;&gt;Piskel&lt;/a&gt; as my Playdate-centric graphics editor. It’s a really useful tool. So I drew a few emoji-like patterns, keeping in mind that they would be converted into a cluster of points and exploded from an origin. This took a bit of experimentation but I ended up with a sort of already exploded look.&lt;/p&gt;

&lt;p class=&quot;screen&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yoyozo-emoji.png#pixel&quot; alt=&quot;EMOJI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To get the pixel data out of Piskel in text form I make use of its “export as a C file” feature. This results in code definitions similar to the below. (I use a script that does some simple regex search/replace to reformat these definitions to be a little more succinct and readable in my Lua code.)&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define EMOJI-TABLE-11-11_FRAME_COUNT 3
#define EMOJI-TABLE-11-11_FRAME_WIDTH 11
#define EMOJI-TABLE-11-11_FRAME_HEIGHT 11
&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* Piskel data for &quot;emoji-table-11-11&quot; */&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emoji&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;121&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x00&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;pixels-as-points&quot;&gt;Pixels as points&lt;/h2&gt;

&lt;p&gt;With this data in hand, my plan was to convert them into points expressed as an angle and distance from an origin. This way of expressing points is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Polar_coordinate_system&quot;&gt;polar coordinate system&lt;/a&gt; so there was no need to invent anything, I just needed to code a function that would take a grid of pixels expressed as &lt;em&gt;(x, y)&lt;/em&gt; and convert them to a series of distances and angles expressed as &lt;em&gt;(r, θ)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The way I do this is to read the point data in from a grid of pixels, offsetting that data by half the width and height of the &lt;em&gt;odd-sized&lt;/em&gt; grid so that the centre of the grid &lt;em&gt;(0, 0)&lt;/em&gt; is the middle of the centre pixel. And finally I convert those adjusted &lt;em&gt;(x, y)&lt;/em&gt; values to polar &lt;em&gt;(r, θ)&lt;/em&gt; coordinates. This worked really well!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Below is a work-in-progress GIF captured on 14 September 2023, shortly after getting the feature working. This animation also shows an early version of the HUD and debug values for ball size and the length of the beam.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yoyozo-emoji.gif#playdate&quot; alt=&quot;EMOJI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The finishing touches (not shown in the above animation) were to add some small amount of &lt;em&gt;pseudo-randomisation&lt;/em&gt; to the initial rotation of the emoji, the initial “colour” of each particle, and changing the coordinates of each point slightly so they appear more organic and move at slightly different speeds. I eventually settled on over a dozen such pixel patterns in the game (how many have you spotted?). Once your score is higher than 50M points every explosion is an emoji! &lt;a href=&quot;https://play.date/games/yoyozo/#gameListingMoreInfo&quot;&gt;Check out the game manual&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;pseudo-random&quot;&gt;Pseudo-random?&lt;/h2&gt;

&lt;p&gt;I try to limit my use math.random values if I can help it, or at least use it in controlled way. I already use that for the positions of the stars, so if I also used it for explosions that would mean it would become far less controlled. Controlling the use of random is important in making a game system deterministic, if you want it to react the same way every time.&lt;/p&gt;

&lt;p&gt;If you’re wondering how you can get pseudo-random values, the main method I use is a trick I learned from the old arcade game &lt;a href=&quot;/2011/10/26/flicky-1984/&quot;&gt;Flicky (1984, SEGA)&lt;/a&gt; which is a game &lt;a href=&quot;https://www.flicky1984.com/post/709058873877790720/just-a-quick-reminder-that-you-can-play-my-flicky&quot;&gt;I own as a physical cabinet&lt;/a&gt;. Anyway, in Flicky there is a diamond that appears under seemingly random conditions.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;One of the MAME programmers was kind enough &lt;a href=&quot;https://www.flicky1984.com/post/54534135892/the-best-jewel-thief-in-the-world&quot;&gt;reverse engineer Flicky on my behalf and figured out what makes the diamond appear&lt;/a&gt;. It turns out the diamond will appear only if you knock out an enemy cat and it disappears outside of the centre third of the play area &lt;em&gt;and&lt;/em&gt; if the x coordinate at which the cat finally comes to rest is even. So, about 50% chance &lt;em&gt;but&lt;/em&gt; only if you position the screen correctly during play.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, taking a cue from that wonderful Flicky logic, I use modulus—which returns the remainder of a division—as my main mechanism of generating pseudo-random values. It ensures a value in the range &lt;em&gt;[0,n-1]&lt;/em&gt;. They key thing to note is that if you base it on values in your game system that are constantly changing you can get seemingly random values that have the benefit of being deterministic if the player is skilled enough at repeating their inputs. The deterministic thing is how pretty much all the old school arcade games operated from Pac-Man to Flicky and more.&lt;/p&gt;

&lt;p&gt;Common game variables I use are: game tick (my alternative to timer), x-coordinate, y-coordinate, speed, angle, or combinations of more than one of these. I also used this approach in YOYOZO for the starfield particles, and in &lt;a href=&quot;/2023/04/13/sparrow-solitaire-for-playdate/&quot;&gt;Sparrow Solitaire&lt;/a&gt; for the falling particles that make up the &lt;a href=&quot;https://www.reddit.com/r/PlaydateConsole/comments/12vcrm6/dynamic_weather_effects_and_more_in_the_sparrow/&quot;&gt;weather effects&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;playdate-particles&quot;&gt;Playdate particles&lt;/h2&gt;

&lt;p&gt;There are &lt;a href=&quot;https://github.com/PossiblyAxolotl/pdParticles&quot;&gt;one or more libraries&lt;/a&gt; available that can be used to manage particles in a performant way on Playdate. Though I tend to code my own system that is bespoke to the game I’m working on at the time (I start each game from a blank file and use minimal libraries, force of habit). But the important thing for such limited platforms, especially when using Lua, is to use a pool of particles so that you’re not constantly creating and destroying particles which would wreak havoc on performance through overuse of Lua’a garbage collector.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt; (or, how I made a Playdate game in 39KiB)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/12/09/dynamic-music-and-sound-techniques-for-video-games/&quot;&gt;Dynamic music and sound techniques for video games&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.date/games/yoyozo/#gameListingMoreInfo&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt; manual/player’s guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 26 Nov 2023 20:41:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/11/26/easter-egg-emoji-converting-pixels-into-particles/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/11/26/easter-egg-emoji-converting-pixels-into-particles/</guid>
        </item>
      
    
      
        <item>
          <title>YOYOZO (or, how I made a Playdate game in 39KB)</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;2023-12-27—&lt;a href=&quot;https://arstechnica.com/gaming/2023/12/ars-technicas-best-video-games-of-2023/7&quot;&gt;Ars Technica: YOYOZO wins GOTY accolade!&lt;/a&gt; almost unbelievable to be listed alongside such games as: Chants of Sennaar, Cocoon, Dave the Diver, Humanity, The Legend of Zelda: Tears of the Kingdom, Pikmin 4, Puzzmo, Super Mario Bros. Wonder, Venba and Viewfinder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A game I made for the Playdate handheld was released today! &lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;Go buy it&lt;/a&gt; and then come back to read this blog post.&lt;/p&gt;

&lt;p&gt;It’s called YOYOZO and in it you control a space yo-yo and have to collect stars in a sort of cosmic ballet. Well, at first it might feel a little like being on a fairground ride, but eventually you’ll become good enough for it to feel like ballet. The concept is based on my memory of a game called &lt;a href=&quot;https://archive.org/details/Pendulumania-v1.3&quot;&gt;Pendulumania&lt;/a&gt; that I played 20 years ago.&lt;/p&gt;

&lt;p&gt;The most amazing thing about this game, for me, is that launch version weighs in at a file size of &lt;em&gt;only 39KiB&lt;/em&gt;. I’m using the KiB unit of measurement which equates to 1024 bytes. I still find it hard to believe as the game contains so much! In this blog post I’ll go into some of the nerdy details.&lt;/p&gt;

&lt;h2 id=&quot;playdate&quot;&gt;Playdate?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; is a handheld gaming system with a unique crank input method. I don’t use the crank in this game, but I have done in the past and will do again in the future.&lt;/p&gt;

&lt;p&gt;If you own a Playdate you can buy the game now at &lt;a href=&quot;https://play.date/games/yoyozo/&quot;&gt;play.date/games/yoyozo/&lt;/a&gt;. If you don’t own a Playdate, well, &lt;a href=&quot;https://play.date&quot;&gt;what are you waiting for&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yoyozo-teaser.gif#playdate&quot; alt=&quot;YOYOZO&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;butwhy&quot;&gt;But…&lt;em&gt;why?&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;The drive to produce a small game started after I sent the first playable version to testers. Steve at &lt;a href=&quot;http://scenicroutesoftware.com&quot;&gt;&lt;em&gt;Scenic Route Software&lt;/em&gt;&lt;/a&gt;, purveyor of quality video games, commented how tiny the game was. At that point it was 18KiB, but had no music or sound effects or polish. There was a long way to go.&lt;/p&gt;

&lt;p&gt;Even so, I wondered how doable it would be to build the game out with an eye on keeping file size “low”. I thought back to the days of my youth where whole games would fit on a single floppy disk, with room to spare. If they could do it, shouldn’t I give it a try?&lt;/p&gt;

&lt;p&gt;It’s worth noting that even with this mindset, I didn’t make a huge sustained effort to meet the goal. On the contrary, it was just something I simply kept in mind as development proceeded. For that reason, I’m sure there are more ways the game could be made even smaller than it is, with the exact same code and content. For example, I never tried finding the most optimal format for things like music and particle data which are the two largest sets of embedded data.&lt;/p&gt;

&lt;p&gt;Finally, this is not a challenge, or me throwing down the gauntlet in any way. It’s easy enough to make a smaller game, be it similar or entirely different, you’d just have to make different choices along the way. This was just me doing something nerdy as an additional constraint on top of the already enjoyable constraints of developing for Playdate.&lt;/p&gt;

&lt;p&gt;That said, I think every game developer should regularly make a point of writing code for an underpowered device as part of their own personal development—there are so many lessons to learn.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;reasons&quot;&gt;Reasons&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;The main reason for the small file size is the fact that &lt;em&gt;the game does not use any digital sound files, and very few bitmap images&lt;/em&gt; (the launch card and animation have to be bitmaps, and in-game only the logo and fonts are bitmaps). Game graphics are all drawn using only shapes (lines, rects, circles) and fills (black, white, and dither patterns).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A second reason is that whilst I use the base Playdate Lua SDK, &lt;em&gt;I don’t use any of the additional “CoreLibs”&lt;/em&gt;. The only extra graphics functions I needed were for drawing outlined or filled circles, so I use two of my own wrapper functions that are similar to those from CoreLibs/graphics but mine are smaller and more specific. For timers, I use a simple frame/tick system, an approach which has pros and cons, but it’s good enough for me.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A third reason is that I made the tough decision to reduce system assets, which means &lt;em&gt;there is no animated launcher card&lt;/em&gt;. This was a tough one, but it added so much to the file size I decided against it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Finally, I noticed that &lt;em&gt;including data inside your game code&lt;/em&gt; often trumps how well you can compress it and store it externally. For example I tried compressing the music data and storing it in an external file, but the game final file size was larger than if I embedded the data in my Lua code. Plus, it’s faster as it doesn’t need to load an additional external file.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;breakdown&quot;&gt;Breakdown&lt;/h2&gt;

&lt;p&gt;I thought it would be cool to outline the main features and how each contributes to the total file size. Note that the sizes are expressed as quantities of the compiled binary, rather than uncompiled source code. It’s also worth noting that a blank project with an empty update function results in a compiled binary of only 147 bytes. Playdate compiles to Lua bytecode.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yoyozo-chart.png&quot; alt=&quot;CHART&quot; /&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Content&lt;/th&gt;
      &lt;th&gt;Kilobytes&lt;/th&gt;
      &lt;th&gt;%&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Main code&lt;/td&gt;
      &lt;td&gt;19&lt;/td&gt;
      &lt;td&gt;49&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Two music tracks&lt;/td&gt;
      &lt;td&gt;5.5&lt;/td&gt;
      &lt;td&gt;14&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Three bitmap fonts&lt;/td&gt;
      &lt;td&gt;2.5&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Synthesized sound effects&lt;/td&gt;
      &lt;td&gt;2.5&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom particle system&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Animated system icon&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Animated system card&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pulp music engine (modified)&lt;/td&gt;
      &lt;td&gt;1.5&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Online scoring system&lt;/td&gt;
      &lt;td&gt;0.5&lt;/td&gt;
      &lt;td&gt;1.25&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;How to play instructions&lt;/td&gt;
      &lt;td&gt;0.4&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Custom soundtrack capability&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
      &lt;td&gt;0.25&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;“Main code” contains: physics simulation, game structure and state management, multi-layered scoring and bonus system, score/stat tracking, loading and saving stats and settings, path recording and playback, animated introduction, plus the following &lt;em&gt;dynamic&lt;/em&gt; systems: scrolling starfield, screen shake, music system, sound effects system. All running at 40fps.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;abandoned-and-removed-features&quot;&gt;Abandoned and removed features&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I tried a bunch of stuff during development. Such as asteroid fields or meteor showers that introduced obstacles that needed to be avoided, and black holes that would magnetically attract the ball. But I felt they detracted from the pureness of the concept, so I didn’t go any further with them.&lt;/li&gt;
  &lt;li&gt;The positions of stars are randomly generated, but I have implemented a fixed “daily” layout in the game, which is really fun. It’s a different experience to be able to play the same layout over and over, improving your execution of the same moves and eking out higher and higher scores. I did plan to reintroduce that option when Playdate Catalog got score boards that reset daily, but by the time that happened &lt;a href=&quot;/2025/04/15/when-playdate-stopped-being-fun/&quot;&gt;I was no longer developing Playdate&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;manualguide&quot;&gt;Manual/Guide&lt;/h2&gt;

&lt;p&gt;I really loved the manuals that came with games in the 8-bit and 16-bit era. So I thought it would be fun to write a manual/player’s guide in the old-school style. I love reading those sorts of manuals, where the developer gives you a little glimpse behind the curtain so you get an understanding of how the game works, with some small hints and tips littered throughout—for the most inquisitive players! If that sounds like your thing, &lt;a href=&quot;https://play.date/games/yoyozo/#gameListingMoreInfo&quot;&gt;download the manual from the game page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://play.date/games/yoyozo/#gameListingMoreInfo&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yoyozo-manual.png&quot; alt=&quot;YOYOZO Manual&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;timeline&quot;&gt;Timeline&lt;/h2&gt;

&lt;p&gt;I worked on YOYOZO from September 5th to 27th, submitting it to Catalog on 21st and polishing it for the final week after that. After the game was approved I added online score boards one evening just prior to launch. It was in review and waiting for release longer than it was in development!&lt;/p&gt;

&lt;p&gt;The purpose of this section is not to say that developing a game quickly is better than developing one slowly, or vice versa, but rather to show the importance of scoping a game well and then sticking to the plan.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2023-09-05—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1699108587732119834&quot;&gt;initial prototype&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-09-07—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1699784106505290093&quot;&gt;playable prototype&lt;/a&gt; (3 days)&lt;/li&gt;
  &lt;li&gt;2023-09-07—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1699890693366517890&quot;&gt;quick progress&lt;/a&gt; (3 days)&lt;/li&gt;
  &lt;li&gt;2023-09-09—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1700612152707461396&quot;&gt;came up with the name&lt;/a&gt; (5 days)&lt;/li&gt;
  &lt;li&gt;2023-09-14—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1702103698749505670&quot;&gt;polishing and balancing&lt;/a&gt; (10 days)&lt;/li&gt;
  &lt;li&gt;2023-09-20—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1704608465522487681&quot;&gt;revelatory physics tweak&lt;/a&gt; (16 days)&lt;/li&gt;
  &lt;li&gt;2023-09-21—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1704991183573831711&quot;&gt;addicted to my own game&lt;/a&gt; (17 days)&lt;/li&gt;
  &lt;li&gt;2023-09-21—submitted to Catalog (17 days)&lt;/li&gt;
  &lt;li&gt;2023-09-23—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1705676134245875750&quot;&gt;game over replay&lt;/a&gt; (19 days)&lt;/li&gt;
  &lt;li&gt;2023-09-26—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1706765228879253972&quot;&gt;layout design using spreadsheet&lt;/a&gt; (22 days)&lt;/li&gt;
  &lt;li&gt;2023-09-26—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1706772586510643560&quot;&gt;game over stats screen&lt;/a&gt; (22 days)&lt;/li&gt;
  &lt;li&gt;2023-09-27—final version (23 days)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and then some waiting until:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2023-10-10—approved for Catalog (36 days)&lt;/li&gt;
  &lt;li&gt;2023-11-19—added online scoreboards (76 days)&lt;/li&gt;
  &lt;li&gt;2023-11-21—&lt;a href=&quot;https://twitter.com/gingerbeardman/status/1727030817116053611&quot;&gt;released on Catalog&lt;/a&gt; (78 days)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…so that is 78 days (11 weeks) from initial prototype to being live on the Catalog store!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;/h2&gt;

&lt;p&gt;YOYOZO is a game by Matt Sephton, with music by Jamie Hamshere.&lt;/p&gt;

&lt;p&gt;Thanks to CANO-Lab and Testers.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/11/26/easter-egg-emoji-converting-pixels-into-particles/&quot;&gt;Easter egg emoji: converting pixels into particles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/12/09/dynamic-music-and-sound-techniques-for-video-games/&quot;&gt;Dynamic music and sound techniques for video games&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.date/games/yoyozo/#gameListingMoreInfo&quot;&gt;&lt;em&gt;YOYOZO&lt;/em&gt; manual/player’s guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;elsewhere&quot;&gt;Elsewhere&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;2024-10-16—&lt;a href=&quot;https://gamerepublic.net/news/best-indie-game-made-in-the-north-of-england-2024-award/&quot;&gt;Game Republic 2024 Awards: Best Indie Game Made in the North of England&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2024-03-08—&lt;a href=&quot;https://play.date/games/community-awards-2023/&quot;&gt;Playdate Community Awards 2023: Best Arcade Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-12-27—&lt;a href=&quot;https://arstechnica.com/gaming/2023/12/ars-technicas-best-video-games-of-2023/7&quot;&gt;Ars Technica: YOYOZO wins GOTY accolade!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-11-30—&lt;a href=&quot;https://arstechnica.com/gaming/2023/11/my-long-quest-to-revive-a-90s-windows-gaming-cult-classic/&quot;&gt;Ars Technica&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-11-24—&lt;a href=&quot;http://eepurl.com/iEHB8M&quot;&gt;Hacker Newsletter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-11-23—&lt;a href=&quot;https://www.timeextension.com/news/2023/11/yoyozo-is-a-new-playdate-game-inspired-by-the-japanese-cult-classic-pendulumania&quot;&gt;Time Extension&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-11-22—&lt;a href=&quot;https://news.ycombinator.com/item?id=38372936&quot;&gt;Hacker News&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-11-22—&lt;a href=&quot;https://tildes.net/~games/1cbz/yoyozo_or_how_i_made_a_playdate_game_in_39kb&quot;&gt;Tildes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 21 Nov 2023 23:59:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/</guid>
        </item>
      
    

  </channel>
</rss>
