<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>seated.ro</title>
    <link>https://seated.ro</link>
    <description><![CDATA[programmer by day, programmer by night.]]></description>
    <atom:link href="https://seated.ro/rss.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Thu, 01 Jan 2026 00:00:00 UT</lastBuildDate>
    <item>
      <title>glimpses of the future</title>
      <link>https://seated.ro/posts/glimpses-of-the-future.html</link>
      <description><![CDATA[<h2>Glimpse v1.0</h2>
<p><a href="https://github.com/seatedro/glimpse">Glimpse</a> can now build call graphs, showing you exactly how functions relate to each other in your codebase.</p>
<p><img src="https://media.seated.ro/glimpse.mp4" alt=""></p>
<pre><code class="language-bash"># what does main call?
glimpse code main.rs:main

# what calls this function? (reverse call graph)
glimpse code requests.py:process_request --callers

# limit the depth
glimpse code :build --depth 3
</code></pre>
<p>This works by parsing your code with tree-sitter, extracting function definitions and calls, then resolving those calls to their actual definitions.</p>
<h3>Precise mode</h3>
<p>Sometimes tree-sitter based resolution isn't enough. Maybe you're dealing with dynamic dispatch, generics, or just a language with particularly complex module resolution. For this, Glimpse can use LSPs to resolve definitions semantically.</p>
<pre><code class="language-bash">glimpse code :main --precise
</code></pre>
<p>This spins up actual LSP servers and uses goto-definition / goto-implementation to resolve calls. It's slower, but accurate. Glimpse will attempt to auto-install the LSP servers for you.</p>
<h3>Indexing</h3>
<p>Glimpse eagerly caches whatever it finds into an incremental index. But you can choose to pre-build the index ahead of time for instant queries.</p>
<pre><code class="language-bash"># build the index
glimpse index build

# with LSP for precise resolution
glimpse index build --precise

# check what you've got
glimpse index status
</code></pre>
<p>The index stores all the definitions, calls, and resolutions so subsequent queries are fast.</p>
<h3>Language support</h3>
<p>Glimpse now supports: Go, Rust, C, C++, Python, TypeScript, JavaScript, Zig, Java, Scala, Nix, Lua, Ruby, C#, Kotlin, Swift, and Haskell.</p>
<p>Each language has custom tree-sitter queries for extracting definitions, calls, and imports. The grammars are downloaded and compiled automatically on first use.</p>
<h2>Try it</h2>
<pre><code class="language-bash"># install
cargo install glimpse

# or with homebrew
brew tap seatedro/glimpse &amp;&amp; brew install glimpse

# or with nix
nix profile install github:seatedro/glimpse

# then just
glimpse code :main
</code></pre>
]]></description>
      <pubDate>Thu, 01 Jan 2026 00:00:00 UT</pubDate>
      <guid>https://seated.ro/posts/glimpses-of-the-future.html</guid>
      <dc:creator>seatedro</dc:creator>
    </item>
    <item>
      <title>Devlog 001</title>
      <link>https://seated.ro/posts/devlog-001.html</link>
      <description><![CDATA[<p>This is a general devlog covering what I’ve been up to over the past few months!</p>
<h2>Haskell</h2>
<p>Hakyll is a Haskell library for generating static websites (yes, you do not, in fact, need Next.js for your personal site).</p>
<p>The previous iteration of my website was written with Go + templ, and it was not static. I wrote everything myself, and it was, quite frankly, a horror to maintain. The templ LSP actually hinders more than it helps, and since I did not statically generate the content, it was kind of slow for a personal website. I did not like it.</p>
<p>I added some very minor CSS fixes and a theme toggle button for my friend's new website ludwigabap.com, and I saw the light that is Hakyll. :kneel:</p>
<p>So I nerd-sniped myself into rewriting my personal website in Hakyll so that it generates beautiful HTML at build time (it uses Tailwind, too). All the dynamic bits of the website, like my silly stats and coding time, are served from a single Rust Axum server binary on my Hetzner VM that serves other stuff too (Memegrep’s server, among other things).</p>
<p><img src="/images/hakyll.png" alt=""></p>
<h2>Nix-pill</h2>
<p>This segues into my next nerd-snipe, or rather, my favorite new pill: the Nix pill.</p>
<p>My entire website is built with a single Nix flake, including the Hakyll build step, the Cargo build step, and generating the resulting Docker image. Although, to be honest, the learning curve is very steep and I simply do not have it in me to master this language anytime soon, I like the principles, and it seems to me the least bad build system out there.</p>
<p>I do most of my work on a MacBook running a NixOS VM as well. (seatedro/dotnix on GitHub.) It’s so much nicer to have a real dev workflow versus whatever the fuck macOS is. Sorry, but Homebrew is not good software.</p>
<h2>Ember</h2>
<p>The reason I started programming was that I was playing a game called Midtown Madness as a wee lad and decided that one day I would build a cool game like that. So, after 15 years, I’ve decided to embark on a small adventure to build a cool physics simulation (and eventually a voxel) engine. It’s written entirely in Zig because I enjoy writing Zig and did not want to touch C++ (although there were times I thought about committing this sin).</p>
<p>When Ember gets far enough, I might write separate devlogs, but with what’s written so far, there isn’t enough to justify one.</p>
<p>I was knee-deep in the mines because I wanted extremely specific features like multi-viewport docking with ImGui, and the off-the-shelf Zig libraries did not want to ship that for some reason. So there were many adventures in getting this shit to compile neatly.</p>
<p>Ember now uses SDL3 for windowing and has an abstract rendering API (rudimentary, but the backends are in place) with SDLRenderer3, OpenGL, and WGPU-native backends that can be neatly switched at <code>comptime</code>.</p>
<p>I’m using a bunch of cool resources to learn more about how to do this because I’ve never really written any game-dev related code before.</p>
<p>I just want to build some cool simulations, and I will get there no matter what.</p>
<p>github / seatedro / ember</p>
<h2>Thanatos</h2>
<p>After 5 years, I have finally built a new personal computer. I will be running NixOS as my main operating system with Hyprland and Wayland. I will also have Windows installed on a separate drive mainly for video games, though I expect to use Linux more often.</p>
<p><img src="/images/thanatos.png" alt=""></p>
<p>It’s so nice not having to worry about my 256 GB SSD getting filled up on my MacBook Air. :&gt;</p>
<h2>Exa</h2>
<p>Also, I joined Exa last month to work on the back-end team, and it has been so fun! I am soaking up as much knowledge from my significantly smarter peers at a good pace.</p>
<p>As per custom, I revisited @ludwigABAP’s post: <a href="https://ludwigabap.com/posts/On%20becoming%20competitive%20when%20joining%20a%20new%20company">On becoming competitive when joining a new company</a>.</p>
<p>(PS: it’s on a new website.)</p>
<h2>Fin</h2>
<p>Back to the code cave I go—going to extract the polymorphic.</p>
]]></description>
      <pubDate>Sun, 13 Jul 2025 00:00:00 UT</pubDate>
      <guid>https://seated.ro/posts/devlog-001.html</guid>
      <dc:creator>seatedro</dc:creator>
    </item>
    <item>
      <title>If you don&apos;t tinker, you don&apos;t have taste</title>
      <link>https://seated.ro/posts/tinkering-a-lost-art.html</link>
      <description><![CDATA[<h2>tin·ker</h2>
<h4>/ˈtiNGkər/</h4>
<h4>to make small changes to something, especially in an attempt to repair or improve it.</h4>
<h1>In Hindsight</h1>
<p>Growing up, I never stuck to a single thing, be it guitar lessons, art school, martial arts – I tried them all. when it came to programming, though, I never really tinkered. I was always amazed with video games and wondered how they were made but I never pursued that curiosity.</p>
<p>My tinkering habits picked up very late, and now I cannot go by without picking up new things in one form or another. It's how I learn. I wish I did it sooner. It's a major part of my learning process now, and I would never be the <s>programmer</s> person I am today.</p>
<h1>What the hell is tinkering?</h1>
<p>Have you ever spent hours tweaking the mouse sensitivity in your favorite FPS game?</p>
<p>Have you ever installed a Linux distro, spent days configuring window managers, not because you had to, but purely because it gave you satisfaction and made your workflow exactly yours?</p>
<p>Ever pulled apart your mechanical keyboard, swapped keycaps, tested switches, and lubed stabilizers just for more thock?</p>
<p>That is what I mean.</p>
<p>I have come to understand that there are two kinds of people, those who do things only if it helps them achieve a goal, and those who do things just because. The ideal, of course, is to be a mix of both.</p>
<blockquote>
<p>when you tinker and throw away, that's practice, and practice should inherently be ephemeral, exploratory, and be frequent - <a href="https://x.com/ludwigABAP/status/1842366186824032737">@ludwigABAP</a></p>
</blockquote>
<h1>My approach to tinkering</h1>
<p>There are plenty of people who still use the VSCode terminal as their default terminal, do not know what vim bindings are, GitHub desktop rather than the cli (at the very least). I'm not saying these are bad things necessarily, just that this should be the minimum, not the median.</p>
<p>This does not mean I spend every waking hour fiddling with my neovim config. In fact, the last meaningful change to my config was 6 months ago. Finding that balance is where most people fail.</p>
<p>Over the years I have done so many things that in hindsight have made me appreciate programming more but were completely &quot;unnecessary&quot; in the strict sense.</p>
<p>In the past week I have, for the first time, written a glsl fragment shader, a rust procedural macro, template c++, a swift app, furthered my hatred for windows development (this is not new), and started using the helix editor more (mainly for good defaults + speed). I didn't have to do these things, but I did, for fun! And I know more about these things now.</p>
<p>No time spent learning, is time wasted.</p>
<h1>Why taste matters, especially now</h1>
<p>Acquiring good taste comes through using various things, discarding the ones you don't like and keeping the ones you do. if you never try various things, you will not acquire good taste.</p>
<p>And what I mean by taste here is simply the honed ability to distinguish mediocrity from excellence. This will be highly subjective, and not everyone's taste will be the same, but that is the point, you should NOT have the same taste as someone else.</p>
<p>Question the status quo, experiment, break things, do this several times, do this everyday and keep doing it.</p>
]]></description>
      <pubDate>Sat, 15 Mar 2025 00:00:00 UT</pubDate>
      <guid>https://seated.ro/posts/tinkering-a-lost-art.html</guid>
      <dc:creator>seatedro</dc:creator>
    </item>
    <item>
      <title>rabbit hole learning</title>
      <link>https://seated.ro/posts/rabbit-hole-learning.html</link>
      <description><![CDATA[<h2>Look everything up</h2>
<p>Pretty much the best way to learn that I have found is to refrain from suprressing your innate curosity and let it go wild. Most of the time, you <em>will</em> encounter a term or concept that you do not know, instead of glancing at it briefly, go all in.</p>
<p>Google it, read the wiki page, found someone's blog post? Read it. Watch that youtube video (I only do this if it's not 3 hours like your average Sphaerophoria stream).</p>
<p>Dive head first into rabbit holes.</p>
<p>I'll outline an average day where I have two types of goals:</p>
<ul>
<li>Concrete goals: Like finish implementing X feature in Y project</li>
<li>Loose goals: Learn about X or Y.</li>
</ul>
<p>I will include rough timestamps but they are pretty much meaningless because productivity levels vary immensely throughout the day.</p>
<h4><strong>9:00 AM</strong></h4>
<ul>
<li>Decided to learn about document parsing.</li>
<li>Links: <a href="https://www.w3.org/TR/epub-33/">EPUB specification</a>,</li>
<li>I found out that EPUBs are just an archive with HTML, and nearly had a crisis. (Now I know.)</li>
<li>Immediate questions: so I need to parse the EPUB, extract metadata, fetch the XML and parse that too. Eventually I would have to use a WebView to render the HTML/CSS on the screen to render the book. (Unfortunate.)</li>
<li>Found out that in order to read the PDF spec, you need to pay like 350 swiss francs!</li>
<li>Spend time perusing SwiftUI docs, and asking grok about how I can render things on the screen with Swift.</li>
<li>Fiddle with XCode, marvel at how Swift gets pretty much everything right but uses <code>func ...</code> for declaring functions. It has <code>Result&lt;T, E&gt;</code> though, so forgiven.</li>
</ul>
<h4><strong>11:00 AM</strong></h4>
<ul>
<li>Began reading <a href="#csapp">Computer Systems: A Programmer's Perspective</a> (CSAPP)</li>
<li>Studied numeric representations, created Anki cards for hexadecimal conversion</li>
</ul>
<h4><strong>12:00 PM</strong></h4>
<ul>
<li>Rabbit holed into learning so much about <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a></li>
<li>Bookmarked to learn more about UTF-8 and UTF-16 and writing a parser for it later (ILY <a href="https://x.com/zack_overflow">@zack_overflow</a>)</li>
</ul>
<h4><strong>1:00 PM</strong></h4>
<ul>
<li>Taking a break, lunch + watching a movie.</li>
</ul>
<h4><strong>2:30 PM</strong></h4>
<ul>
<li>Read Jon Olick's single file <a href="https://www.jonolick.com/uploads/7/9/2/1/7921194/jo_resize.h"> resize implementation in C++ </a> as reference (ILY <a href="https://x.com/gizmobly">@gizmobly</a>) to roll my own resize library for use in <a href="https://github.com/seatedro/glyph">glyph</a></li>
<li>Rabbit holed into learning more about <a href="https://en.wikipedia.org/wiki/Sinc_function">Sinc filters</a> and the <a href="https://en.wikipedia.org/wiki/Lanczos_resampling"> Lanczos Kernel </a>.</li>
</ul>
<h4><strong>3:30 PM</strong></h4>
<ul>
<li>Shifted to working on <a href="https://memegrep.com">memegrep</a>(v2). This is where I already know the goals I want to accomplish before the day ends.</li>
<li>I implemented a pub/sub flow to help with scale when users upload their private memes.</li>
<li>Rough sketch in mind:<pre><code>user uploads meme(s) → server receives req → insert skeleton into db → 
queue upload → return 201 to user immediately → worker picks up task
</code></pre>
</li>
<li>Spent the next 6 hours coding without even realizing 6 hours had passed (bliss)</li>
<li>Ended up with multi-file upload, search, deployed a CLIP model, added all the scaffolding needed in the UI for this.</li>
</ul>
<h4><strong>9:30 PM</strong></h4>
<ul>
<li>I posted something about pointers on twitter and ended up reading some history about the nomenclature just for fun.</li>
<li>Links: <a href="https://en.wikipedia.org/wiki/Pointer_(computer_programming)">pointers</a>, <a href="https://floooh.github.io/2018/06/17/handles-vs-pointers.html">handles are the better pointers</a></li>
<li>Also saw something about reference counting being used in the libvips library earlier in the day, so decided to check out their implementation since i've only used it in rust quite often. <a href="https://en.wikipedia.org/wiki/Reference_counting">reference counting</a>, <a href="https://doc.rust-lang.org/rust-by-example/std/rc.html">Rc</a> and <a href="https://doc.rust-lang.org/std/sync/struct.Arc.html">Arc</a></li>
</ul>
<h4><strong>11:00 PM</strong></h4>
<ul>
<li>Was in bed and saw a post linking an amazing article by Valve on <a href="https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking">Source Engine Networking</a>, so ended up being a nice and light read.</li>
</ul>
<hr>
<p>At the end of the day I ended up with more questions, but I definitely had more answers than when I started!</p>
<p>Here's a <a href="https://kroki.io/mermaid/svg/eNptU8tu2zAQvPMr-AOBgZ56KhA7dusiLoQoRg9EUVD0SmYjkQQfcdqv74qUTCqxDhQ5u1zuY6bt9UWcufX0-YFQ_FxoOsvNmR60VVJ1ERw_aLR-YdtxpU_AT2B_ZZsJDdtWxzVd0ephR50Pbbs0_3YGRPKpcZeN5tQm23hxaXIX2XpWjyu9N6aXgnupVXGXWweWrbnF7BQ4usWXaBXR7CUcN4Zt6vuqipljVdl4hjf2Dd6wHiEH3tONVq9gHT7jslMjFbd_2Tr-MIax4ED5mE3hFnzLjs-7u88JAnWK_2Vf71sPVmmtcnQL_IWtxzXHwmQ6YPtxpZXVApxb5P3HQMe-V9uvU7mrn1b6smpMUfAe2CY4rwdMOh7po2wsFlE0WSrBalzoLiix7G_PlfinHXtM_zEIH3AOcyK3K9y-woI5AwzQWTDsMG3o66dihqHBm6wKzaoOTdFM02t-cuwQei8NZr6TuBwTWqQP3IozO2IL6Bif1hEopt9LHP7jvkJGn6AvHpDsuKebM1cdFAGtYE_QggUlANkQlF_03WipsM-OVdOm7Ph0zeUIZaYe-MBqHSwG3qpOKqA_wF-0fbnd0Kg4enf3JQqIzEq6IlE2S3gWE1nILpqimMhVVsk9UufWi1Ez5KqeiKFUyCSZeE6qIFkgEUURkEkMyWvkdX4iHqMhMpxcuR6xkdXvoInHpOB0qgcpS2YCR2Ria_JM0DwtUo5uCjtPKOeWH50pS0r-poiRriQzNxWdaPnRPdGTZKam7iInP_oG-R_AkKEc">DAG</a> of my exploration for fun:</p>
]]></description>
      <pubDate>Sun, 09 Mar 2025 00:00:00 UT</pubDate>
      <guid>https://seated.ro/posts/rabbit-hole-learning.html</guid>
      <dc:creator>seatedro</dc:creator>
    </item>
    <item>
      <title>two weeks is all you need</title>
      <link>https://seated.ro/posts/my-opinions-on-claude.html</link>
      <description><![CDATA[<p><img src="/images/two-weeks.webp" alt="two-weeks"></p>
<p>I built a website (twoweeksisallyouneed dot com) with just Claude 3.5 Sonnet, zero lines of code written by me.</p>
<h3>Why I did this</h3>
<p>So a couple weeks back I had a computer vision midterm and i was allowed a single page of notes. I decided to use Claude to generate a cheat sheet in LaTeX. It was crazy lol, I was able to cook up something usable in 15 mins.</p>
<blockquote class="twitter-tweet" data-theme="dark"><p lang="en" dir="ltr">claude is the best, this was made in 15 minutes <a href="https://t.co/0tvIsdv34d">pic.twitter.com/0tvIsdv34d</a></p>&mdash; ro/nin (@seatedro) <a href="https://twitter.com/seatedro/status/1849557895081299979?ref_src=twsrc%5Etfw">October 24, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<br/>
<p>Someone had asked me to make a similar cheatsheet for ML, but I thought why not get claude to make an entire website instead?</p>
<h3>Early days</h3>
<p>I didn't really give claude any specific information except what I wanted to build. My plan was as follows:
&gt; build out the UI skeleton first
&gt; populate with some dummy data
&gt; set up a content pipeline
&gt; tie up the ui with the data
&gt; fix bugs
<br/>
Claude decided to use react (Shocking).
With just a couple of chats I was able to get the retro/hacker/matrix style UI down. (It's going to be hard for frontend engineers to keep up with AI at this rate)</p>
<br/>
I spent some time building and adding secrets/easter eggs to the website which no one has found yet lmaoooo. If/when all the easter eggs are found, I will open source the repository. Good hunting until then bros.
<h3>Frustrations</h3>
<p>Things got really annoying, really fast. As soon as I wanted to build some sort of content pipeline, everything went to shit. Claude, no matter how smart of an AI, is not human. It did not think ahead. If I was going to build out this website I would not have started with a barebones react/vite app. I would have probably gone for a full stack framework like sveltekit instead. Generating content for the website was/is a nightmare.</p>
<br/>
<p>LLMs hallucinate, this is known, but did you also know how incredibly frustrating it is to get them to follow instructions? There were multiple instances where Claude (with all the 40% of project context of javascript code) generated a TypeScript interface and proceeded to spit TypeScript code. This project has 0 TypeScript????</p>
<br />
<p>The new edit in place nonsense was getting on my nerves. Half the time the output artifact wouldn't even change, and the other half it would mess up the changes. I have to mention &quot;please use a new artifact&quot; if I wanted any real usable code.</p>
<br />
<p>I was enthusiastic about building something with just Claude the first few days, then I started getting weary, and then eventually I wanted to take a sabbatical from using AI.</p>
<br />
When the project context grows, (think like 20% or more), Claude seems to have a hard time using that information. Often times I found myself hitting send, and Claude would spit some nonsense, I would hit stop and paste any relevant code directly and then get some useful code. Context is probably the biggest annoyance I've had with LLMs.
<h3>Random thoughts</h3>
<p>Perplexity (with claude 3.5) is great (to an extent) because it's essentially a RAG search so I was able to get somewhat up to date content for the resources and references sections from it.</p>
<br/>
I feel like I was able to ship something of decent quality for sure, but I lost a lot of brain cells during the process. Do not take away from the programmer the only thing he wishes to do, <i>program</i>.
<h3>Proompting</h3>
<p>I didn't particularly do anything unique to get the best out of my prompts/chats. I did do everything in a Project on claude dot ai though, which let me set Project Instructions like so:</p>
<pre><code class="language-plaintext">BE ENTHUSIASTIC. WE ARE GOING TO CHANGE THE WAY THE WORLD LEARNS WITH THIS WEBSITE🚀🚀🚀🚀
</code></pre>
<p>LLMs seem to be much more open to doing anything you ask if you gaslight them, so go ahead and do it.</p>
<br/>
Here are some prompts that I used while building the website:
<br/>
<pre><code class="language-plaintext">PROMPT
------
This is AWESOME:
* For the loading screen add some text that says &quot;you can learn anything in two weeks&quot;
* The ascii text is also weird, it says erain? is that supposed to mean something?
* Where did all my placeholder topics go?
* MOAR scrt scan lines
* The matrix rain needs to fall vertically and it should be subtle. On mouse move it should get a bit brighter (the char under the mouse)

What else do you think you could add? Surprise me
</code></pre>
<p>One time I asked Claude to add something and it started changing the existing UI?????</p>
<pre><code class="language-plaintext">PROMPT:
------
OKAY, we have a beautiful boot sequence now.

let's flesh out the main content screen.

First let's fix some bugs:
* The animations restart everytime i move my mouse instead of continue organically
   * What i mean is, when i move my mouse over the windows or around the matrix rain, the matrix rain restarts and so do the typewriter effect on the window title
Work on these
* [REDACTED] Easter eggs:
* Fun Features:
   * &quot;Power saving mode&quot; that dims everything except what you're reading
    ....[REDACTED]
</code></pre>
<p>So many times I ended up editing the prompt and adding lines like this:</p>
<pre><code>PLEASE DON'T CHANGE THE EXISTING UI. IT LOOKS GOOD BRO.
</code></pre>
<p>I'll just add some random prompts here</p>
<pre><code class="language-plaintext">PROMPT
------
Broski, we need to make the UI responsive and shit.

Lot of people are reporting issues with it on mobile and smaller screens

PROMPT
------
what sort of content do u think we should add? i was thinking things like formulae, charts (for phd), code blocks (for eng), research papers, youtube video links, blog posts etc.

Let's think out loud how we're gonna do this before we proceed

PROMPT
------
`pasted_code.jsx`
Here is my current dashboard.

Here is some sample concept content i have:
`pasted_data.json`

I need you to render this beautifully in the dashboard.

Make any and all changes needed.

It needs to look beautiful.

And anything else u feel would be good.

List out the things you're adding before adding it okay?
</code></pre>
<h3>Concluding thoughts</h3>
<p>This was a good experiment, a success. If you have a clear vision for what you want your product to be, then AI can help you achieve that vision quite well!</p>
<br/>
<p>However, I don't think I will be using AI for the foreseeable future. I feel like my learning is stagnating the more i use AI and I want to write my slop code with my own two hands. I might use <code>avante.nvim</code> to quickly write some duplicated code here and there but by god, I miss coding. Actual coding.</p>
]]></description>
      <pubDate>Thu, 21 Nov 2024 00:00:00 UT</pubDate>
      <guid>https://seated.ro/posts/my-opinions-on-claude.html</guid>
      <dc:creator>seatedro</dc:creator>
    </item>
    <item>
      <title>a weekend love story - raylib/zig</title>
      <link>https://seated.ro/posts/asteroids-zig-raylib.html</link>
      <description><![CDATA[<p><img src="/images/zig-raylib/steroids.zig.webp" alt="raylib-zig-emscripten"></p>
<h3>Zig Psyop</h3>
<p>Before this weekend, I was a plebeian who used JavaScript for 80% of my tasks. Now I am an esoteric plebeian who has used zig once. Anyway, I decided to give zig a shot and try to build a game with it. There was really only two libraries I wanted to learn (sokol and raylib), I went with raylib.
<br/></p>
<h3>Initial References</h3>
<p>I went through the official raylib <a href="https://www.raylib.com">docs</a> to get a feel for the API, then I searched for zig bindings and found two, <a href="https://github.com/Not-Nik/raylib-zig">Not-Nik/raylib-zig</a> and <a href="https://github.com/ryupold/raylib.zig">ryupold/raylib-zig</a></p>
<p>The second one hasn't been updated in 6 months, and it isn't using the zig package manager. Ergo, leaving me no choice but to use the first one.</p>
<h3>Setup</h3>
<p>Fairly easy to set things up, I just needed to install zig and raylib-zig.</p>
<blockquote>
<p><em>NOTE</em> : We will be using zig v0.12.0 instead of v0.13.0, we will see why later.</p>
</blockquote>
<p>I used <a href="https://github.com/tristanisham/zvm">zvm</a> to make life easier mangaging various zig versions, but you can install zig however you want.</p>
<p>Follow this <a href="https://github.com/Not-Nik/raylib-zig#installation">part</a> of the docs to install raylib-zig. Do this inside your game directory. This adds raylib-zig as a dependency to our project. You can check the <code>build.zig.zon</code> file to verify.</p>
<br/>
<p>Now the most important part if you want to compile your game for the web with emscripten. <em>IF YOU'RE NOT BUILDING FOR THE WEB, PLEASE SKIP</em>.</p>
<br/>
<p>Install <code>emsdk</code> as mentioned here: <a href="https://emscripten.org/docs/getting_started/downloads.html">emscripten installation guide</a> and make sure to do the <code>source ./emsdk_env.sh</code> command.</p>
<blockquote>
<p><em>NOTE</em> : Just make sure not to clone the emsdk into $HOME/.emscripten, because emscripten uses that as the default cache directory. It will fuck your build up. (I didn't do this at all)</p>
</blockquote>
<p>If you followed the raylib-zig installation guide, your build.zig should look something like this:</p>
<br/>
<pre><code class="language-zig">const std = @import(&quot;std&quot;);

pub fn build(b: *std.Build) !void {
    const target = b.standardTargetOptions(.{});

    const optimize = b.standardOptimizeOption();

    const exe = b.addExecutable(.{
        .name = &quot;steroids.zig&quot;,
        .root_source_file = b.path(&quot;src/main.zig&quot;),
        .target = target,
        .optimize = optimize,
    });

    const raylib_dep = b.dependency(&quot;raylib-zig&quot;, .{
        .target = target,
        .optimize = optimize,
    });

    const raylib = raylib_dep.module(&quot;raylib&quot;);
    const raylib_artifact = raylib_dep.artifact(&quot;raylib&quot;);

    exe.linkLibrary(raylib_artifact);
    exe.root_module.addImport(&quot;raylib&quot;, raylib);

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);

    run_cmd.step.dependOn(b.getInstallStep());

    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step(&quot;run&quot;, &quot;Run the app&quot;);
    run_step.dependOn(&amp;run_cmd.step);
}
</code></pre>
<br/>
<p>If it isn't, it should be now! Now we need to add a block specific to the emscripten target.</p>
<br/>
<pre><code class="language-zig">// add these two imports
const fs = std.fs;
const rlz = @import(&quot;raylib-zig&quot;);

pub fn build(b: *std.Build) !void {
    // ... other code

    const raylib = raylib_dep.module(&quot;raylib&quot;);
    const raylib_artifact = raylib_dep.artifact(&quot;raylib&quot;);

    if (target.query.os_tag == .emscripten) {
        const exe_lib = rlz.emcc.compileForEmscripten(b, &quot;steroids.zig&quot;, &quot;src/main.zig&quot;, target, optimize);

        exe_lib.linkLibrary(raylib_artifact);
        exe_lib.root_module.addImport(&quot;raylib&quot;, raylib);

        const include_path = try fs.path.join(b.allocator, &amp;.{ b.sysroot.?, &quot;cache&quot;, &quot;sysroot&quot;, &quot;include&quot; });
        defer b.allocator.free(include_path);
        exe_lib.addIncludePath(.{ .path = include_path });
        exe_lib.linkLibC();

        // linking raylib to the exe_lib output file.
        const link_step = try rlz.emcc.linkWithEmscripten(b, &amp;[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact });

        // Use the custom HTML template
        // This will be the index.html where the game is rendered.
        // You can find an example in my repository.
        link_step.addArg(&quot;--shell-file&quot;);
        link_step.addArg(&quot;shell.html&quot;);

        // Embed the assets directory
        // This generates an index.data file which is neede for the game to run.
        link_step.addArg(&quot;--preload-file&quot;);
        link_step.addArg(&quot;assets&quot;);

        link_step.addArg(&quot;-sALLOW_MEMORY_GROWTH&quot;);
        link_step.addArg(&quot;-sWASM_MEM_MAX=16MB&quot;);
        link_step.addArg(&quot;-sTOTAL_MEMORY=16MB&quot;);
        link_step.addArg(&quot;-sERROR_ON_UNDEFINED_SYMBOLS=0&quot;);
        // Add any other flags you need

        b.getInstallStep().dependOn(&amp;link_step.step);
        const run_step = try rlz.emcc.emscriptenRunStep(b);
        run_step.step.dependOn(&amp;link_step.step);
        const run_option = b.step(&quot;run&quot;, &quot;Run the game&quot;);
        run_option.dependOn(&amp;run_step.step);
        return;
    }

    // rest of the build script
}
</code></pre>
<br/>
<p>I missed this code block and was stuck trying to figure out how to get the emscripten run step to work. :(</p>
<h3>Write some code!</h3>
<p>I wrote an asteroids clone following along with this video: <a href="https://www.youtube.com/live/ajbYYgbDXGk?si=J4GzjKmHucSjhkaT">zig space rocks - jdh</a>. Go write whatever game you want!</p>
<br/>
<p>Here is a screenshot of the game in action:
<br/></p>
<p><img src="/images/zig-raylib/ingame.webp" alt="space rocks"></p>
<br/>
<p>Here are some other cool games made in zig: <a href="https://github.com/jlafayette/zig-tetris/blob/master/src/main.zig">tetris</a>, <a href="https://github.com/JosefAlbers/TerrainZigger">terrain-zigger</a></p>
<br/>
<p>Below is some cool zig code I wrote that I quite like:</p>
<br/>
<pre><code class="language-zig">rl.initAudioDevice();
defer rl.closeAudioDevice();
sound = Sound{
    .asteroid = rl.loadSound(&quot;assets/asteroid.wav&quot;),
    .bloop_lo = rl.loadSound(&quot;assets/bloop_lo.wav&quot;),
    .bloop_hi = rl.loadSound(&quot;assets/bloop_hi.wav&quot;),
    .explode = rl.loadSound(&quot;assets/explode.wav&quot;),
    .shoot = rl.loadSound(&quot;assets/shoot.wav&quot;),
    .thrust = rl.loadSound(&quot;assets/thrust.wav&quot;),
};

rl.setSoundVolume(sound.explode, 0.5);

// this unwraps the loop at compile time for each field in the Sound struct
// and frees the memory allocated for each sound
defer inline for (std.meta.fields(Sound)) |f| {
    rl.unloadSound(@field(sound, f.name));
};

defer state.asteroids.deinit();
defer state.asteroids_q.deinit();
defer state.particles.deinit();
defer state.projectiles.deinit();
defer state.aliens.deinit();
</code></pre>
<br/>
<p>This <code>defer</code> syntax is so fucking cool, I will never forget to <code>free</code> again.</p>
<br/>
<pre><code class="language-zig">const bloop_intensity: usize = @min(@as(usize, @intFromFloat(state.now - state.stage_start)) / 16, 4);
const bloop_mod: usize = 60;
const adjusted_bloop_mod: usize = @max(1, bloop_mod &gt;&gt; @intCast(bloop_intensity));

if (state.frame % adjusted_bloop_mod == 0) {
    state.bloop += 1;
}

if (!state.ship.isDead() and state.bloop != state.last_bloop) {
    rl.playSound(if (state.bloop % 2 == 0) sound.bloop_hi else sound.bloop_lo);
}
state.last_bloop = state.bloop;
</code></pre>
<br/>
<p>This code is for deciding when to play the low/high bloop sound. It increases in intensity the longer a player is alive. Makes it feel like the game is getting harder. Pretty cool!
Also avoided for loops with bit shifting. absolutely unnecessary. But I did it anyway.</p>
<h3>Memory management</h3>
<p>One thing I think is important is for some reason I wasn't able to just use a <code>std.heap.GeneralPurposeAllocator</code> for the heap allocations. So i reused some code from the <a href="https://github.com/ryupold/examples-raylib.zig/blob/main/src/allocator.zig">ryupold/examples-raylib.zig</a> repo and made a custom emscripten allocator.</p>
<br/>
<pre><code class="language-zig">// i modified only one line
// line 50
const c = @cImport({
    @cDefine(&quot;__EMSCRIPTEN__&quot;, &quot;1&quot;);
    @cInclude(&quot;emscripten.h&quot;); // &lt;- I changed it to just emscripten.h
    @cInclude(&quot;stdlib.h&quot;);
});
</code></pre>
<br/>
<p>Without this, I was getting <code>Out of Memory</code> issues. I also increased the memory limit to 16MB. Idk which of those fixed it, but it works now! After that, I was sitting at a solid <code>1.23 MB</code> of memory usage, so I think I was good.</p>
<br/>
<p><img src="/images/zig-raylib/heaptrace.webp" alt="memory usage"></p>
<br/>
<h3>Build the game</h3>
<p>You can build the game for desktop with <code>zig build run</code>. For the web, you need to run the following command:</p>
<br/>
<pre><code class="language-bash">zig build -Dtarget=wasm32-emscripten --sysroot &quot;/absolute/path/to/emsdk/upstream/emscripten&quot;
# the absolute path is crucial, otherwise it will not work.
# now to run the game, you can use the following command:
emrun ./zig-out/htmlout/index.html
</code></pre>
<br/>
<p>Now, as to why we used <code>v0.12.0</code>. For some reason the build command above fails for the emscripten target with <code>v0.13.0</code> as mentioned in this open <a href="https://github.com/Not-Nik/raylib-zig/issues/108">issue</a>. If someone can figure out why, please mention it in the issue!</p>
<br/>
<p>You can drop everything in the <code>zig-out/htmlout</code> folder and host it wherever.</p>
<h3>Fin!</h3>
<p>Was a fun start to my zig arc! I hope you guys don't waste time debugging shit like me lol.</p>
<p>May we zig harder every day.</p>
<br/>
<p><a href="https://github.com/seatedro/steroids.zig">Source Code</a></p>
]]></description>
      <pubDate>Thu, 15 Aug 2024 00:00:00 UT</pubDate>
      <guid>https://seated.ro/posts/asteroids-zig-raylib.html</guid>
      <dc:creator>seatedro</dc:creator>
    </item>
  </channel>
</rss>
