I left my job at startup last month to have break and enjoy some time being bored, on the weekend i decided to write a bytebeat player for fun, which i was thinking from long time.
This was my first time with Zig, and I actually enjoyed writing the code, writing Zig feels like C with friendly defaults.
It is a joy to write, I am not fighting borrow checker or fighting with syntax.
You can call this a passion project, there is no goal or agenda behind building this, I built it because I wanted.
Demo(play with sound on): #
Try here: https://kmj-007.github.io/zigbeat/
Bytebeat #
For many people who don't know what is bytebeat, let me explain:
Bytebeat is music generated from short programs. These programs generate PCM audio as a function of time, see one below which is called 42 melody
t*(42&t>>10)
another way to explain, Bytebeat is a form of algorithmic music that uses bitwise operations, to generate sound, with only one variable t representing time. The expression is evaluated for each sample of the 8bits audio output.
in 2011 viznut discovered this by accident, he blogged about it here
Why/How does it work? #
Audio on computers is stored as a list of numbers called samples. Each number represents the amplitude (volume level) at a specific moment in time.
Standard audio uses:
- 16-bit samples at 44.1kHz (CD quality)
 - Each sample is a number between -32768 and 32767
 - Each sample lasts ~22 microseconds
 
Bytebeat uses a simpler format:
- 8-bit samples at 8kHz
 - Each sample is a number between 0 and 255
 - Each sample lasts 125 microseconds
 
This simpler format was chosen because it was the default for early PC sound cards on Linux. The lo-fi quality is part of bytebeat's charm.
for visual learners:

Experience: #
First in my mind it was like, bytebeat is very simple, we enter some expression and it plays some sound, it doesn't sound any rocket science.
It is actually very simple if you make it in javascript, cause it provides eval, which does all the 90% of the work.
but i decided to build it in Zig, so here we go,
I setup my project using old example repo of raylib-Zig examples, but it was outdated, cause i was using 0.15.1,
And i also wanted to support native as well as browser which was reason for picking up raylib in the first place, for which i needed to setup emscripten to compile it to wasm,
I created everything from scratch from editor, to blinking cursor, select, which felt good but made me realize, how much on the abstraction we are standing!
After writing good amount of code in other languages where string was data type in default, I was little surprised that, Zig doesn't support strings, which lead me to some rabbit holes of understanding how string and struct works in Zig and how they are allocated at byte level,it was refresh for my memory to understand underlying things.
you can't switch on string in Zig btw!
good blog if you want to understand about struct and memory in Zig, Everything is a []u8
good thing for this project was, lot of LLM tools sucked at writing the Zig, specifically new syntax which is not that much popular on the internet, such as recently Arraylist initialization syntax has been changed, it no longer takes allocator, so i didn't use any LLM for this project.
Arena Allocator: #
I ended up implementing Pratt Parser for parsing the expression, after my implementation i was getting different sound of native app and different on browser, and i was scratching my head like what was happening.
After debugging i found that in browser my second and third audio callback had lot of delay, then it clicked me that, on every sample i was creating new AST, allocating memory because of which my program was really slow in evulating the vaules to feed the PCM audio!
then after researching a bit, i was doing wrong memory management, in my case when user types expression and it doesn't changes, the memory it requires for AST is constant, it is not going to change, so for this use case i learned about Arena Allocator, which is a memory allocator that allocates memory in large chunks and then divides it into smaller chunks for individual allocations.
After your work is done, you just free the memory at once.
good talk if you want to learn more about Arena Allocator and memory management in Zig
todo: #
While i wanted to make the Zigbeat with the looks of retro 8bitsynth, similar to following image, replacing the piano keys with visualizer

I tried implementing but drawing everything with primitive gradients and draw api from raylib, it was getting nightmare, I joked with my friend about how were they able to do this kind of things in old times they didn’t even have React!, we both laughed,
Then I figured out in games they use something called sprites, which is nothing images, so i picked myself to become designer, here i am trying to draw keys in photoshop:

and i tried to integrate slowly, here is some screens which looks very bad:

but it was taking lot of time, only reason me being bad design, but i guess i will finish this on some another weekend.
Conclusion: #
It was actually really good decision to build with Zig and not javascript, cause i ended up learning lot about parsing and evaluation, from prattparsing to lexer mods, which i don't regret!
if you are more curious about bytebeat, here is a Rick Roll in bytebeat
Star on GitHub: https://github.com/KMJ-007/Zigbeat
Happy Hacking!
Published 
Subscribe for future blogs