Recently I added another project to my list. A 2D sailing-adventure game. Very 80's-90's retro pixel-art style. I decided to write my own using the golang Ebiten library. I've used it for lots of fun prototypes as it can even run in a browser among a long list of other platforms.
Just playing around. I started off with just drawing some rectangles for the sky, sun and water and made a little pixel-art 'sprite' for a boat in Aseprite.
![Pixelated sailboat with orange flag on teal sea under blue sky. Text reads "Pixel Pirates" and "Any key to start" in center.](https://static.wixstatic.com/media/0a88d4_5be75efed5de42de8d7e1eec810eb90e~mv2.png/v1/fill/w_980,h_564,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/0a88d4_5be75efed5de42de8d7e1eec810eb90e~mv2.png)
I made it so the boat could move, added some animated 'waves' to the water and a really terrible 'island' I tried to draw (its hilariously bad). Then I made the boat a bit bigger and made a crudely animated sail. The waves (and objects) use parallax. Stuff near the horizon moves more slowly. To keep calculations down, when waves go off-screen they wrap around to the other side.
![Pixel art of a small boat with an orange sail on a vast ocean under a moonlit night sky, conveying tranquility and isolation.](https://static.wixstatic.com/media/0a88d4_4591af3883bb40ccae6e7568f26ae9e1~mv2.png/v1/fill/w_980,h_551,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/0a88d4_4591af3883bb40ccae6e7568f26ae9e1~mv2.png)
Then I got completely sidetracked.
I wanted the game to have a FM Synthesizer for the music. Something period correct. Low resource-use and tiny file size. Over the period of a day I researched and wrote code for it. Started off with one channel simple beeps, then added multiple instruments. Neat!
![Code snippet in a dark-themed editor, showing a Go function "GenerateWave" with logic for creating audio waveforms. Keywords are highlighted in colors.](https://static.wixstatic.com/media/0a88d4_136f3d9630414a7da528208464024ab7~mv2.png/v1/fill/w_742,h_391,al_c,q_85,enc_auto/0a88d4_136f3d9630414a7da528208464024ab7~mv2.png)
The music format: (note)(sharp# or flat b)(octave) (full, half or quarter note) Slashes are used to combine notes for chords, commas separate notes.
![Code snippet displaying a soft piano chord configuration with attributes: bpm 70, reverb 0.5, delay 0.3, feedback 0.3, and musical notes.](https://static.wixstatic.com/media/0a88d4_ce596c4d3a3d4a33b1578b3d7e2b0b3c~mv2.png/v1/fill/w_372,h_370,al_c,q_85,enc_auto/0a88d4_ce596c4d3a3d4a33b1578b3d7e2b0b3c~mv2.png)
The next step to improve quality was to add something called ADSR. Attack, Decay, Sustain and Release. This helps transform bleeps to something that sounds like a basic synthesizer. It made a huge difference.
![Blue audio waveform on a gray background with a vertical scale from -1.0 to 1.0 on the left. The waveform is dense and symmetrical.](https://static.wixstatic.com/media/0a88d4_75cb60ad408347b5bdf5a2587028b6c6~mv2.png/v1/fill/w_191,h_144,al_c,q_85,enc_auto/0a88d4_75cb60ad408347b5bdf5a2587028b6c6~mv2.png)
![Screenshot of code in a dark-themed editor. It includes comments and loops for attack, decay, sustain, and release phases of audio processing.](https://static.wixstatic.com/media/0a88d4_c090f16f41f04180bc95f78e14692914~mv2.png/v1/fill/w_938,h_433,al_c,q_90,enc_auto/0a88d4_c090f16f41f04180bc95f78e14692914~mv2.png)
Then I added the following: Per song: Tempo and reverb settings Per instrument: Volume, how square or sinusoidal the instrument is. (or hot vs smooth sounding) I actually used ChatGPT o1 to help write a few basic songs to use for the time being. I explained the music format to it as well as some themes and general feedback. This seems good enough for the moment. Back to visuals... I knew the sky and water need some color gradients, the island needed to look correct for being far away and maybe a water reflection. The sun needed a bit of improvement. That actually made a lot more difference that you might expect.
![Pixel art of a small boat with orange flag on a bright blue ocean. Island in distance under sunny sky, giving a calm, serene vibe.](https://static.wixstatic.com/media/0a88d4_bed4d4d4034e483991cb2aeac59f0195~mv2.png/v1/fill/w_980,h_564,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/0a88d4_bed4d4d4034e483991cb2aeac59f0195~mv2.png)
Okay, that is certainly a lot better. Now this looks like:
A off-brand early 90's Sega Genesis / Mega Drive game you had never heard of until grandma bought it for you. Yes, I'm old.
That is technically better than 'early 80's home computer type-in game' that we started with. Progress, I guess. Okay, I blatantly dated myself there. It's my own fault now.
Next up, I think the sky needs clouds. The clear blue sky is nice, but having a variety of clear-to-cloudy and tiny wisps up to big leviathan clouds going by will really help the visual interest. We could just draw a few different clouds and let them slowly glide through. Nah, lets over complicate it.
Enter Perlin Noise:
So what is perlin noise you ask? Pure magic. Used in games throughout the decades. A huge number of games use this for giant, random and virtually endless worlds. You can share a 'seed number' and even get the same world on another computer. Completely random, but also predictable where needed, it's kind of wild really. Heck of a segue from clouds huh? Used in: Minecraft, No Man's Sky, Rouge, Terraria, Dwarf Fortress, all the Civilization games, Diablo, Factorio and tons more. Okay, what does this have to do with clouds? Well, plain and unfiltered perlin noise is basically an endless landscape of an infinite variety of clouds. We might also use it for random elements in the game but it also just... does this:
![Pixel art of a small wooden boat with an orange sail in a blue sea, sun above, and a distant island on the horizon. Tranquil vibe.](https://static.wixstatic.com/media/0a88d4_c00511fdeacf486babc3f697800fe691~mv2.png/v1/fill/w_980,h_564,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/0a88d4_c00511fdeacf486babc3f697800fe691~mv2.png)
I added a subtle reflection of the clouds on the water. Nice!
![Michael Jordan "stop it" meme.](https://static.wixstatic.com/media/0a88d4_fc479adcce9c422182e25a49463b6745~mv2.gif/v1/fill/w_480,h_358,al_c,pstr/0a88d4_fc479adcce9c422182e25a49463b6745~mv2.gif)
Time To Optimize
Okay, I know people say 'don't optimize early'... but in my defense I was unsupervised. At this point my hacked together code was actually using some computer power. I decided I'd rather fix it now than later The sky and water gradient is capable of nice smooth color transitions for weather and time... but it really doesn't need to perform those calculations every frame.
Simple solution: Render to a 1 pixel wide image when/if we change the colors, and just stretch that across the screen. The GPU will just render this for almost nothing. Next up: The clouds are awesome, but again it is also a lot of calculations if you do it 60+ times a second. We really don't need to do that here, because they go by at a very slow pace being so far away. The solution: Render the clouds to tiled images. Now we can render a quarter of the sky at a time every half-minute or so as the player sails. The debug labels here show the top-left corners of the tiles. position: tile id
![Pixel art of a small boat with an orange flag on blue sea under a sunny sky. An island in the distance. Text with coordinates is visible.](https://static.wixstatic.com/media/0a88d4_b2ef8f550cc64164afec1f848d009bca~mv2.png/v1/fill/w_980,h_564,al_c,q_90,usm_0.66_1.00_0.01,enc_auto/0a88d4_b2ef8f550cc64164afec1f848d009bca~mv2.png)
That is it for the moment. There are so many directions I can go from here. Subscribe below to get notified when I post my next update! Feel free to discuss, suggest ideas or ask questions in the comments below.
Video clip with improved synthesizer:
https://youtu.be/kgDA7aP356w
Commentaires