<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Karl Zylinski's Newsletter]]></title><description><![CDATA[Sign up for my newsletter about Odin, game development, my open source projects, game jams, and more!]]></description><link>https://news.zylinski.se</link><image><url>https://substackcdn.com/image/fetch/$s_!jZgx!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56652d33-d704-4f58-9134-ca4b5c59c2d0_400x400.png</url><title>Karl Zylinski&apos;s Newsletter</title><link>https://news.zylinski.se</link></image><generator>Substack</generator><lastBuildDate>Wed, 15 Apr 2026 20:33:14 GMT</lastBuildDate><atom:link href="https://news.zylinski.se/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Karl Zylinski]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[karlzylinski@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[karlzylinski@substack.com]]></itunes:email><itunes:name><![CDATA[Karl Zylinski]]></itunes:name></itunes:owner><itunes:author><![CDATA[Karl Zylinski]]></itunes:author><googleplay:owner><![CDATA[karlzylinski@substack.com]]></googleplay:owner><googleplay:email><![CDATA[karlzylinski@substack.com]]></googleplay:email><googleplay:author><![CDATA[Karl Zylinski]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Karl2D audio support, more YouTube... and a gamejam?]]></title><description><![CDATA[A summary of what went into adding audio support to Karl2D]]></description><link>https://news.zylinski.se/p/karl2d-audio-support-more-youtube</link><guid isPermaLink="false">https://news.zylinski.se/p/karl2d-audio-support-more-youtube</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Tue, 14 Apr 2026 14:39:31 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/bbd3b076-014a-46e4-a0e6-4a02e1a5b283_2560x1440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello there! Let me update you on what has happened the last few months. I also have an announcement of an announcement of a gamejam &#128579;</p><h2>Karl2D</h2><p>In the previous newsletter I announced the beta of my Karl2D game creation library.</p><p>Since then, I have been hard at work with adding audio support. I&#8217;m proud to finally have shipped the <a href="https://github.com/karl-zylinski/karl2d/releases/tag/beta-3">Beta 3: Audio support release</a>!</p><p>A lot of different kind of work went into that release. The first thing was to play basic sounds and write a software mixer. I wrote <a href="https://zylinski.se/posts/audio-in-karl2d-software-mixing/">a blog post</a> about how I created the software mixer.</p><p>Other things I had to add to the library was:</p><ul><li><p>Audio streaming</p></li><li><p>Possibility to play several sounds simultaneously</p></li><li><p>Backends for Windows, Linux, Mac and Web</p></li><li><p>Support for both mono and stereo audio</p></li><li><p>Smooth volume changes to avoid cracks and pops</p></li></ul><p>I think making my own software mixer was worth it: The audio backends for each platform are very small. They just give the samples to the audio API for that platform, and the way the audio sounds is identical on all platforms.</p><h3>Sponsor Karl2D</h3><p>Want to help me develop Karl2D faster? Sponsor me <a href="https://github.com/sponsors/karl-zylinski">on GitHub</a> or <a href="https://www.patreon.com/karl_zylinski">Patreon</a> &#8212; You don&#8217;t have to be a sponsor forever! If you just want to sponsor for a few months in order to help during this period of rapid development, then that is super helpful! &#128150;</p><h2>Book update</h2><p>Since the last newsletter, I&#8217;ve done two updates of my book &#8220;Understanding the Odin Programming Language&#8221;: Version 1.9 and 1.10. You&#8217;ll find the change list for both updates <a href="https://odinbook.com/#release_notes">here</a>.</p><p>Version 1.9 addressed changes due to <code>core:os/os2</code> becoming the new <code>core:os</code> package. Version 1.10 addressed changes due to <code>Small_Array</code> becoming <code>[dynamic; N]T</code>.</p><h2>A return to YouTube</h2><p>Those who watch my YouTube channel may have been sad that I didn&#8217;t post much during the last few months&#8230; That is now changing! I am again trying to make more videos and host more live streams. Here are two recent videos.</p><p>A video on my Karl2D audio support:</p><div id="youtube2-HYwE8_aElwg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;HYwE8_aElwg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/HYwE8_aElwg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>A video with Odin release highlights (I&#8217;m going to do one of these for every Odin release!)</p><div id="youtube2-7Ty59i4H5TY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;7Ty59i4H5TY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/7Ty59i4H5TY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Gamejam</h2><p>This is an announcement of an announcement&#8482;. I am going to host a Karl2D gamejam sometime soon. I don&#8217;t have the complete details ready yet, but follow me on any of these platforms to make sure you don&#8217;t miss the announcement! More details within just a week or two!</p><ul><li><p><a href="https://www.youtube.com/@karl_zylinski">YouTube</a></p></li><li><p><a href="https://discord.gg/4FsHgtBmFK">Discord server</a></p></li><li><p><a href="https://x.com/karl_zylinski">X</a></p></li><li><p><a href="https://bsky.app/profile/zylinski.se">Bluesky</a></p></li></ul><h2>Have a nice day!</h2><p>/Karl Zylinski</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe to not miss any future newsletters. You&#8217;ll get roughly one newsletter per month.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Karl2D beta is here!]]></title><description><![CDATA[With Windows, Linux, Mac and Web support]]></description><link>https://news.zylinski.se/p/karl2d-beta-is-here</link><guid isPermaLink="false">https://news.zylinski.se/p/karl2d-beta-is-here</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Fri, 23 Jan 2026 07:13:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/cc5f5ea1-e497-4b8b-b8a9-78587862ccb2_1456x1108.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi everyone! Since the last newsletter I&#8217;ve released a beta version of the &#8220;Karl2D&#8221; library!</p><p>It&#8217;s a 2D game creation library written in Odin that tries to be beginner friendly while also minimizing the number of dependencies. The focus is &#8220;easy to get started with, easy to ship the game&#8221;.</p><blockquote><p>&#8220;easy to ship&#8221; actually never happens&#8230; Shipping games is always hard. What I mean is: Less problems due to the library you use, and easier to fix the problems when they do occur!</p></blockquote><p>You&#8217;ll find the library here: <a href="https://github.com/karl-zylinski/karl2d">https://github.com/karl-zylinski/karl2d</a></p><p>In the beta version that I just released, the feature set is as follows:</p><ul><li><p>Rendering of shapes, textures and text with automatic batching</p></li><li><p>Support for shaders and cameras</p></li><li><p>Windows support (D3D11 and OpenGL)</p></li><li><p>Mac support (OpenGL)</p></li><li><p>Linux support (OpenGL)</p></li><li><p>Web support (WebGL, no emscripten needed!)</p></li><li><p>Input: Mouse, keyboard, gamepad</p></li></ul><h2>Ok, that&#8217;s cool. Show me some examples!</h2><p>A basic &#8220;hellope world&#8221; example would be this:</p><pre><code>package hello_world

import k2 "karl2d"

main :: proc() {
&#9;k2.init(1280, 720, "Greetings from Karl2D!")

&#9;for k2.update() {
&#9;&#9;k2.clear(k2.LIGHT_BLUE)
&#9;&#9;k2.draw_text("Hellope!", {50, 50}, 100, k2.DARK_BLUE)
&#9;&#9;k2.present()
&#9;}

&#9;k2.shutdown()
}</code></pre><p>And it would look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uFn0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uFn0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 424w, https://substackcdn.com/image/fetch/$s_!uFn0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 848w, https://substackcdn.com/image/fetch/$s_!uFn0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 1272w, https://substackcdn.com/image/fetch/$s_!uFn0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uFn0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png" width="1456" height="816" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:816,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59384,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/185446126?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uFn0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 424w, https://substackcdn.com/image/fetch/$s_!uFn0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 848w, https://substackcdn.com/image/fetch/$s_!uFn0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 1272w, https://substackcdn.com/image/fetch/$s_!uFn0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c5a8d-a423-43af-89d0-2ce576f1c0b2_1903x1066.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Or you can see it running in a browser <a href="https://zylinski.se/karl2d/hellope/">here</a>.</p><p>That&#8217;s not all that fun though. It&#8217;s just a piece of text, nothing interactive! Here are some more advanced live web examples, with their source code linked:</p><p><a href="https://zylinski.se/karl2d/basics/">Basics example</a> &#8212; Source <a href="https://github.com/karl-zylinski/karl2d/blob/master/examples/basics/basics.odin">here</a> (draw textures and simple movement code)</p><p><a href="https://zylinski.se/karl2d/camera/">Camera example</a> &#8212; Source <a href="https://github.com/karl-zylinski/karl2d/blob/master/examples/camera/camera.odin">here</a> (movable and rotatable camera, thank you to a contributor for making this example)</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XJhD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XJhD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 424w, https://substackcdn.com/image/fetch/$s_!XJhD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 848w, https://substackcdn.com/image/fetch/$s_!XJhD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 1272w, https://substackcdn.com/image/fetch/$s_!XJhD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XJhD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png" width="1456" height="1108" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1108,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:892809,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/185446126?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XJhD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 424w, https://substackcdn.com/image/fetch/$s_!XJhD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 848w, https://substackcdn.com/image/fetch/$s_!XJhD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 1272w, https://substackcdn.com/image/fetch/$s_!XJhD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c500999-713c-48e2-b821-43c010f1ca62_2124x1616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">How the Camera example looks</figcaption></figure></div><p>In the library&#8217;s README I show how to make these web builds. You can make both ordinary desktop games as well as web builds. The web builds will be very handy for game jam creations and such. The fact that they do not need emscripten makes the whole process a lot smoother, compared to creating Raylib web builds.</p><p>If you try the library out and want to show what you made, do please <a href="https://discord.com/invite/4FsHgtBmFK">join my Discord server</a> and post a video or web build link! :)</p><h2>Platform architecture shenanigans</h2><p>In previous newsletters I&#8217;ve talked about avoiding extra dependencies in order to make things more understandable and easily modifiable for <em>you</em>, the game dev!</p><p>I have some new fresh examples of this in this beta version. The current platforms are Windows, Linux, Mac and Web. </p><p>The Windows platform code you can see in <a href="https://github.com/karl-zylinski/karl2d/blob/master/platform_windows.odin">platform_windows.odin</a> &#8212; That one implements the windowing, events and gamepad support. It&#8217;s plain win32 API code without any other library in-between. If you have a Windows-only bug and you are stressed because you need to ship your game <em>soon</em>, then don&#8217;t worry. You can just go in and poke in that Windows platform code without affecting any other platform. No need to recompile some additional C library in debug mode and look for bugs in there. Odin libraries come as source, so you just make changes in the Karl2D source and recompile your game. Easy as PI.</p><p>The Linux platform code is quite different. You can see it in <a href="https://github.com/karl-zylinski/karl2d/blob/master/platform_linux.odin">platform_linux.odin</a> &#8212; There is no windowing code in there! The reason for this is that Linux doesn&#8217;t have a single windowing system. These days people use both X11 and Wayland. So instead of throwing in some bindings to some C library that talks to both, me and a contributor have added support for both! In <a href="https://github.com/karl-zylinski/karl2d/blob/master/platform_linux_window_x11.odin">platform_linux_window_x11.odin</a> and <a href="https://github.com/karl-zylinski/karl2d/blob/master/platform_linux_window_wayland.odin">platform_linux_window_wayland.odin</a> you see the windowing code that the Linux platform layer calls into, depending on which is currently used.</p><blockquote><p>Adding Wayland support wasn&#8217;t all that easy because Wayland is <em>strange</em>. It&#8217;s the new fancy Linux thing for managing windows that is supposed to replace X11. In order to talk to it we had to make bindings for something called <code>libwayland</code>.</p><p><code>libwayland</code> isn&#8217;t much of a library: It&#8217;s just a client that can throw things to the Wayland server using the Wayland protocol. In order to figure out what is possible to communicate using the protocol you <em>parse XML files</em> that tell you what is available&#8230; Yes&#8230; Parse XML files, to generate code, in order to use a protocol, in order to open a window&#8230; &#128576;</p><p>A much better idea would have been a plain C library!! They could make an extension mechanism for it and adding versioning of APIs wouldn&#8217;t be hard either.</p><p>I was literally laughing while working on the Wayland windowing. It was like a parody YouTube video about over-engineering. But it&#8217;s the new thing!! The thing that is going to save Linux and start The Year of Linux on the Desktop!!! AAAAAAAAAAAAaaaa</p><p>Wayland really is the worst windowing API I&#8217;ve ever used. Win32 and even X11 feels like a refreshing day in the sun in comparison.</p><p>Anyways. We managed to tame the Wayland beast. Phew.</p></blockquote><p>You may say: &#8220;But if you used SDL or GLFW then you would just have 1 library to talk to!! Now you have Win32, Wayland, X11 and more. Isn&#8217;t that <em>adding dependencies</em>? You wanted to avoid dependencies!! But you are ADDING MORE!!!&#8221;</p><p>In my opinion, the &#8220;quote&#8221; above is the wrong way to think about this. If you think about it, SDL and GLFW has dependencies on Win32, X11, Wayland etc. So if you use for example GLFW, then you still have dependencies on Win32, X11 and Wayland, but with another another layer in-between. That extra layer is another dependency. And that layer is annoying to change, because it is written in C, and it does lots of things I do not need.</p><p>Anyways, I am happy with how the platform code has turned out. With the help of some contributors, we&#8217;ve been able to also build gamepad support for Mac and Linux. On Linux it goes straight to udev/evdev and talks to the gamepads that way. It&#8217;s pretty cool! It currently supports XBox and PlayStation controllers; give it a go! <a href="https://zylinski.se/karl2d/gamepad/">Here&#8217;s</a> a web version of the gamepad example (the web version does not have vibration currently, I&#8217;ve put that off for some reason).</p><blockquote><p>On Mac we&#8217;ve used the Cocoa framework for windowing and GameController + CoreHaptics for gamepads + vibration. Those are native OS libraries that are included on Mac. They seem to be at a reasonable abstraction level. Thanks to contributors for figuring that stuff out; I don&#8217;t have a recent enough mac to get that work done.</p></blockquote><h2>What&#8217;s missing?</h2><p>This beta (known as Beta 2) does not have the follow features, but they are planned in  the order stated:</p><ul><li><p>Sound</p></li><li><p>System for cross-compiling shaders between different backends (HLSL, GLSL, GLSL ES, MSL etc)</p></li><li><p>Metal rendering backend for Mac (OpenGL already works)</p></li></ul><p>I have a few small tasks to do before starting on sound. My idea for sound is to write a software mixer and then output the audio directly to the low-level audio interfaces of each platform.</p><h2>Support the development of Karl2D</h2><p>If you want to support the development financially, then I&#8217;d be very grateful! You can do so by becoming a sponsor on either <a href="https://github.com/sponsors/karl-zylinski">GitHub</a> or <a href="https://patreon.com/karl_zylinski">Patreon</a>. The money will be used to make me able to spend more hours on this project.</p><h1>Wanna do open-source contributions to odin-c-bindgen?</h1><p>My C library binding generator needs help! I don&#8217;t have time to fix bugs for it currently. If you want to help then please check out the section called <a href="https://github.com/karl-zylinski/odin-c-bindgen?tab=readme-ov-file#contributing">Contributing</a> on its GitHub page. Thank you to all past contributors!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe to get the next newsletter in your inbox. It will be 1-2 months between each newsletter.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Bye bye, have a nice day!</h2><p>/Karl Zylinski</p>]]></content:encoded></item><item><title><![CDATA[Two months worth of Odining]]></title><description><![CDATA[Lots of technical Karl2D goodies!]]></description><link>https://news.zylinski.se/p/two-months-worth-of-odining</link><guid isPermaLink="false">https://news.zylinski.se/p/two-months-worth-of-odining</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Thu, 04 Dec 2025 20:34:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f277c848-b725-406f-be60-91579314cbaf_1920x1225.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi, hello! It&#8217;s been two months since the previous newsletter! This time I&#8217;ll mainly touch on two things that have happened since last time: The release of my bindgen rewrite and details on some Karl2D progress.</p><h2>odin-c-bindgen 2.0 released!</h2><p>I spent most of October rewriting my binding generator from scratch. Why did I do that? Because the code had gotten too messy, it was hard to fix bugs without stacking hacks upon hacks.</p><blockquote><p>odin-c-bindgen takes C library headers and generates Odin bindings. The generator is meant to be easy to configure and produce nice-looking bindings that are easy to navigate while retaining comments.</p></blockquote><p>One problem with the old implementation was that it used libclang things here and there all over the code. There wasn&#8217;t a clear &#8220;pipeline&#8221;. The base design for bindgen 2.0 is that I first fetch everything I need from the C headers using libclang. After that stage libclang isn&#8217;t used at all. From there on, it&#8217;s a lot of processing of the data until it&#8217;s all finally outputted.</p><p>I wanted the generator to be possible to use for two things:</p><ol><li><p>Quickly testing out a C library within an existing Odin code base.</p></li><li><p>Using configuration options to make the bindings as close to production-ready as possible.</p></li></ol><p>(1) is important because there may be 3 different libraries that don&#8217;t have Odin bindings, and you want to quickly see how those libraries would integrate with an existing Odin code base. Then you&#8217;d want to make quick &amp; dirty bindings, just to try things out. If you have to create bindings for each library by hand, then you are actually likely to settle for the wrong library, due to not having the energy to hand-author all those bindings.</p><p>In order to prove that (2) was possible, I decide to try to make Raylib bindings that were as close as possible to the Odin vendor bindings. As I did that, I had to add a few features here and there. You can see the generated Raylib bindings <a href="https://github.com/karl-zylinski/odin-c-bindgen/blob/main/examples/raylib/raylib/raylib.odin">here</a>.</p><blockquote><p>One feature that was important was the &#8220;footer feature&#8221;. Say that you are generating bindings based on the C header <code>raylib.h.</code> In order to make the bindings really nice to use, you may need to do a few custom Odin things. For example, perhaps you want to add a custom Odin allocator that is compatible with the library. I made it so that you can put a <code>raylib_footer.odin </code>file next to the <code>raylib.h</code> file. The stuff in the footer will simply be pasted at the end of the generated bindings. Simple, but effective.</p></blockquote><p>See the release notes for the new version <a href="https://github.com/karl-zylinski/odin-c-bindgen/releases/tag/2.0">here</a>.</p><h2>Karl2D progress</h2><p>The Karl2D library is coming along nicely. I have slightly less time to spend on side-projects recently, due to a part-time contracting job. Nonetheless, some great progress has been made!</p><h3>OpenGL backend</h3><p>I&#8217;ve made an OpenGL backend, meaning that you can now use D3D11 or OpenGL on Windows. Soon I will also add Linux windowing, and on that platform use the OpenGL backend for rendering.</p><blockquote><p>By &#8220;backend&#8221; graphics programmers often refer to the part of their renderer that takes care of talking to the GPU using an rendering API such as OpenGL, Direct3D, Vulkan etc. A library that has multiple backends must be written in such as a way that the shared parts of the library don&#8217;t directly use things from specific rendering APIs. The backend is abstracted away, so that the shared parts of the renderer work no matter which backend is used.</p></blockquote><p>I realized one important thing when making the OpenGL backend. It is a lot nicer to first write a D3D11 backend and then move on to OpenGL. I think D3D11 is the best API for learning graphics programming. It has reasonable amount of features while being very discoverable. If you want to understand a certain feature of the API you can often just look at an API function and then look at any structs that the function refers to. With OpenGL things are not so simple. OpenGL uses lots of generic constants that can be used in very arbitrary ways. It is hard to guess how to do the right thing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Gst4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Gst4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 424w, https://substackcdn.com/image/fetch/$s_!Gst4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 848w, https://substackcdn.com/image/fetch/$s_!Gst4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!Gst4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Gst4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png" width="1456" height="941" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:941,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:945004,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/180644959?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Gst4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 424w, https://substackcdn.com/image/fetch/$s_!Gst4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 848w, https://substackcdn.com/image/fetch/$s_!Gst4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!Gst4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7d3af21-a019-44f3-bbcd-d242fc6d1189_2784x1800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">RenderDoc is a program that lets you capture frames of a running program and inspect the state of the GPU. You can look at the actions that went into creating the captured frame. You can inspect the contents of buffers, meshes and constants.</figcaption></figure></div><p>When implementing the OpenGL backend, a lot of my time was spent on staring at my D3D11 code and trying to come up with something similar in OpenGL. I often did it step-by-step. For example, when setting up the rendering of a draw call, I first got my data uploaded to the GPU. Before writing any more code, I used <a href="https://renderdoc.org/">RenderDoc</a> to check that the data had arrived to the GPU correctly. Then I added code for setting OpenG shaders, uniforms etc. At each step I tried to verify what I had done using RenderDoc. This made graphics programming a lot more fun! No more was it &#8220;write a lot of code and then debug it for 5 hours&#8221;. Instead it became: &#8220;Try to make your GPU do a bunch of tiny tasks and verify each step, if possible&#8221;. This made the code better. It often worked on first try. </p><h3>Creating the OpenGL context</h3><p>Many people use libraries like GLFW in order to create the OpenGL context. The OpenGL context is platform-specific. It&#8217;s the bridge between your OS window and OpenGL.</p><p>I try to minimize the number of dependencies, so naturally I wanted to try without anything like GLFW.</p><blockquote><p>Bear in mind that I also do the windowing myself. On Windows I use the win32 API to create and manage windows. So there is platform-specific windowing code, and then there is a tiny bit of platform-specific OpenGL code that creates the context.</p></blockquote><p>On Windows, creating the context is a lot about picking a compatible &#8220;pixel format&#8221;. You can see the file that implements my Windows OpenGL context <a href="https://github.com/karl-zylinski/karl2d/blob/master/render_backend_gl_windows.odin">here</a>. It&#8217;s not a lot of code, so I&#8217;m happy I didn&#8217;t give up and use GLFW.</p><p>There are a few peculiar things however: In order to get RenderDoc to work with GL I had to use the <code>win32.wglChoosePixelFormatARB</code> procs to help me choose pixel format. Funny thing is: That procedure is an <em>extension</em>, and in order to fetch extensions, you have to already have a valid OpenGL context. So first I have to create an OpenGL context using the &#8220;old school&#8221; way that is incompatible with RenderDoc. Then I have to use that &#8220;dummy context&#8221; to create the context I actually want to use. Silly!</p><h3>Render Textures</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cgcl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cgcl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 424w, https://substackcdn.com/image/fetch/$s_!cgcl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 848w, https://substackcdn.com/image/fetch/$s_!cgcl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!cgcl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cgcl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png" width="1084" height="1140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1140,&quot;width&quot;:1084,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52391,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/180644959?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cgcl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 424w, https://substackcdn.com/image/fetch/$s_!cgcl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 848w, https://substackcdn.com/image/fetch/$s_!cgcl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!cgcl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e2e528-b6bb-4250-ac34-5413c81c23de_1084x1140.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">I&#8217;ve rendered the picture with the square, circle and text once into a Render Texture, and then rendered that texture three times.</figcaption></figure></div><p>I added support for drawing into textures, as opposed to drawing to the screen. You can see how that feature works in <a href="https://github.com/karl-zylinski/karl2d/blob/master/examples/render_texture/render_texture.odin">the render_texture example</a>.</p><blockquote><p>I still need to implement render textures in the GL backend.</p></blockquote><h3>Pre-multiplied alpha</h3><p>In order to make blending look better you often have to use pre-multiplied alpha. This means that you multiply the alpha value of a texture&#8217;s pixels into the RGB values of each pixel. Then you modify how the blending is set up so that it takes this into consideration.</p><blockquote><p>Pre-multiplied alpha can sometimes make your rendering look better. If you notice black fringes around your half-transparent pixels, then chances are that using pre-multiplied alpha would help you.</p></blockquote><p>Many projects start out with using &#8220;normal alpha blending&#8221;, because they don&#8217;t have a pipeline to do the pre-multiplication yet. In my <a href="https://github.com/karl-zylinski/karl2d/blob/master/examples/premultiplied_alpha/premultiplied_alpha.odin">premultiplied_alpha example</a> I show how you can easily get going with pre-multiplied alpha by telling the texture loading procedure to pre-multiply the alpha for you:</p><pre><code><code>tex := k2.load_texture_from_file(&#8221;plop.png&#8221;, options = { .Premultiply_Alpha })</code></code></pre><p>To make it correct use these pre-multiplied textures, you just have to change the blend mode from <code>.Alpha </code>to <code>.Premultiplied_Alpha:</code></p><pre><code><code>k2.set_blend_mode(.Premultiplied_Alpha)</code></code></pre><h3>Setting texture sampling mode</h3><p>When the GPU draws pixels from a texture, it&#8217;s said that it <em>samples</em> them. You can specify sample mode, for example you can use &#8220;point sampling&#8221; to preserve the pixlyness of an image. You can use &#8220;linear sampling&#8221; to smooth the texture when it is has been scaled up.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ugjw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ugjw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 424w, https://substackcdn.com/image/fetch/$s_!Ugjw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 848w, https://substackcdn.com/image/fetch/$s_!Ugjw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!Ugjw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ugjw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png" width="1084" height="1140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1140,&quot;width&quot;:1084,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:589457,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/180644959?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ugjw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 424w, https://substackcdn.com/image/fetch/$s_!Ugjw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 848w, https://substackcdn.com/image/fetch/$s_!Ugjw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!Ugjw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc94c973a-215c-40e6-98d2-3e2d31849e12_1084x1140.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">&#8220;Linear sampling&#8221;: The upscaled picture of the cat is &#8220;blurry&#8221;</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qRc8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qRc8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 424w, https://substackcdn.com/image/fetch/$s_!qRc8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 848w, https://substackcdn.com/image/fetch/$s_!qRc8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!qRc8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qRc8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png" width="1084" height="1140" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1140,&quot;width&quot;:1084,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:162908,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/180644959?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qRc8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 424w, https://substackcdn.com/image/fetch/$s_!qRc8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 848w, https://substackcdn.com/image/fetch/$s_!qRc8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!qRc8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F939c8108-8b61-48d3-9277-4195a19dc47b_1084x1140.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">&#8220;Point sampling&#8221;: The upscaled image of the cat is &#8220;pixly&#8221;.</figcaption></figure></div><p>Point sampling is the default in Karl2D as many people make pixel art games, or just find smoothed textures ugly. I&#8217;ve added so you can set the filtering on a per-texture basis by writing:</p><pre><code>k2.set_texture_filter(tex, .Linear)</code></pre><p>In OpenGL this was fairly simple to implement: For each texture in an GLSL shader, there is a single sampler. So it&#8217;s easy to associate a sampling method with a specific texture.</p><p>In D3D11 however, you create samplers and textures separately in the shader. This means that you can have 1 sampler used by 2 textures. And you can only set the sampling method to a single value&#8230; So now, if someone makes a custom shader, then they could easily end up with a configuration that does not work with per-texture texture filtering configuration: A sampler can&#8217;t be in two states at once!</p><p>I went back and forth a lot on what to do about this. In the end, I decided to implement some verification code in the procedure that loads HLSL shaders: If you have a texture in HLSL texture register 1, then there must be a sampler in HLSL sampler register 1. This makes D3D11 behave a bit more like OpenGL.</p><blockquote><p>I don&#8217;t enforce a single shader programming language. Instead I use the shader language that is native to each rendering API. I will provide default shaders for each shader language. However, if you want to write your own shaders, then you may have to write multiple versions of it.</p><p>However, I do plan to add a cross-compiler toolkit that you can use. But that is a later problem. That cross-compiler will sit outside of Karl2D and Karl2D will accept the result of such a cross-compilation. This makes sure that Karl2D avoids weird assumptions about shader languages and enforces fewer dependencies.</p><p>Another thing to consider is what happens when you need to finally ship a game. Then the idea of having shaders that you can &#8220;write once, run everywhere&#8221; sometimes bites you in the foot. You may very well need to write custom shaders for some platform, in the shading language native to the rendering API it uses. With my solution you can do that when the need arises. This follows my main design idea for Karl2D: &#8220;Easy to get started with, helpful when you need to actually ship a game.&#8221; &#8212; Many &#8220;easy-to-use&#8221; libraries end up being troublesome when you need to ship. I&#8217;m trying to avoid that.</p></blockquote><h3>The future</h3><p>I&#8217;m tinkering with something I haven&#8217;t announced yet. More about that next year.</p><p>I also have a contract job for a few months, so my open-source projects and YouTube productivity has taken a hit. I&#8217;ll be back!</p><p>In the coming months I&#8217;ll try to get Karl2D to a state where I feel comfortable with doing a first big round of tests.</p><p>Have a nice December and happy holidays!</p><p>/Karl Zylinski</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe to get the next newsletter. It&#8217;ll be no more than once a month.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Writing about strings and lots of Karl2D updates]]></title><description><![CDATA[September really was a productive month!]]></description><link>https://news.zylinski.se/p/odin-book-update-sale-karl2d-progress</link><guid isPermaLink="false">https://news.zylinski.se/p/odin-book-update-sale-karl2d-progress</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Thu, 02 Oct 2025 13:05:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f29f45e4-cfc0-454b-a55c-aa1b4f4fd825_2880x1440.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>September really was a productive month! Let&#8217;s talk about what happened.</p><p>We&#8217;ll start with news regarding my book <em>Understanding the Odin Programming Language</em>, then some updates on my 2D game library <em>Karl2D</em> and finally some future plans for my binding generator.</p><h2>Book update: Strings chapter overhaul, and a sale!</h2><p>One big thing that happened in the <a href="https://github.com/odin-lang/Odin/releases/tag/dev-2025-09">September release of Odin</a> was native UTF-16 support using the <code>string16 </code>and <code>cstring16 </code>types. Odin doesn&#8217;t use these types a lot, since it focuses on UTF-8. The UTF-16 types are mainly for interfacing with Windows code (and any other library that needs UTF-16). Nevertheless, I did have a section in my book <a href="https://odinbook.com/">Understanding the Odin Programming Language</a> where I showed how to interface with the Windows API. Now the code in that chapter no longer compiled, so the book needed to be updated!</p><p>Having a native UTF-16 type in the language made me treat UTF-16 a bit more rigorously. As I tried to rewrite those sections, I also noticed that my discussion around the interplay between Unicode and UTF-8 was a bit lacking.</p><p>So I started doing a major overhaul of the Strings chapter of the book! It needed more details on what Unicode actually is, and how UTF-8 is used to encode/decode Unicode. That way I could extend that discussion to UTF-16 in the later parts of the chapter.</p><p>I also added a section on how to manually decode UTF-8. That specific section is freely available <a href="https://zylinski.se/posts/iterating-strings-and-manually-decoding-utf8/">on my blog</a> and also as a YouTube video:</p><div id="youtube2-Zl1Gs8iUpi0" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;Zl1Gs8iUpi0&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/Zl1Gs8iUpi0?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>This update, including some other small fixes, took almost two weeks to complete. The updated Strings chapter (chapter 11) has several almost completely rewritten sections, as well as a few new sections.</p><p>The update is live, with release notes <a href="https://odinbook.com/#release_notes">here</a>.</p><p><em><strong>I am running a sale of the book on <a href="https://store.zylinski.se/">store.zylinski.se</a> &#8212; 25% off until October 9!</strong></em></p><h2>Karl2D progress</h2><p>Since the last newsletter I&#8217;ve been doing good progress on my &#8220;Karl2D&#8221; library! It&#8217;s a 2D game creation library. The API is based on Raylib, but meant to be written in native Odin, using as few dependencies as possible.</p><p>It is not yet ready for use, but here follows some highlights.</p><blockquote><p>Follow the development of the library on <a href="https://github.com/karl-zylinski/karl2d">GitHub</a>.</p></blockquote><h3>More efficient rendering</h3><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;a2245cca-3930-49fc-bd3b-3d48c8337ac4&quot;,&quot;duration&quot;:null}"></div><p>I ported the &#8220;bunnymark&#8221; Raylib demo.</p><p>When running the ported demo, I ran into some errors in how I handle batching. After fix that I could spawn around 160000 bunnies while staying at 60 fps. This is not meant to be a benchmark, as I haven&#8217;t really optimized anything yet. But it&#8217;s looking OK!</p><p>The port is available <a href="https://github.com/karl-zylinski/karl2d/blob/master/examples/raylib_ports/bunnymark/bunnymark.odin">here</a> (original <a href="https://www.raylib.com/examples/textures/loader.html?name=textures_bunnymark">here</a>).</p><p>The batching works by building a vertex buffer that can be up to 1 megabyte large. If it hits the limit, then it does a draw call and then empties the buffer and continues drawing. It&#8217;s similar to what Raylib does. However, I also have the possibility to customize vertex inputs. In Raylib that is hard-coded. This way, you can add extra vertex inputs in the shader and set their value while drawing, without modifying the library.</p><h3>Gamepad support</h3><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;86527539-2bf8-4a77-9cb7-dcac230bfdd3&quot;,&quot;duration&quot;:null}"></div><p>The basic gamepad support uses XInput. I&#8217;ll make sure to get PlayStation controllers to work as well. Later, when I add more platforms, I&#8217;ll use that platform&#8217;s native API to do gamepad support on it.</p><h3>Basic text rendering (work in progress)</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T6bo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T6bo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 424w, https://substackcdn.com/image/fetch/$s_!T6bo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 848w, https://substackcdn.com/image/fetch/$s_!T6bo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 1272w, https://substackcdn.com/image/fetch/$s_!T6bo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T6bo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png" width="1002" height="1046" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1046,&quot;width&quot;:1002,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:42275,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/175091358?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T6bo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 424w, https://substackcdn.com/image/fetch/$s_!T6bo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 848w, https://substackcdn.com/image/fetch/$s_!T6bo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 1272w, https://substackcdn.com/image/fetch/$s_!T6bo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e4bb372-6c90-4b8c-8c5b-dc8013aca7a2_1002x1046.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I use stb_truetype to generate bitmap fonts. Similar to how raylib does it. The offsets and line heights are still a bit wrong in this image, but I&#8217;ll get there. I am thinking if I perhaps should have some fancier way to process fonts, such as dynamically building bitmap atlases. We shall see!</p><blockquote><p>stb_truetype is one of few non-OS / non-rendering APIs that I&#8217;ve used. We shall see if it stays in the long run, but it&#8217;s a simple enough library that I would be OK with the dependency.</p><p>People may wonder what the philosophy is here: Why are some libraries OK while others are not? For example, why am I not just using SDL_GPU for the rendering?</p><p>The answer lies on how heavily your project becomes dependent on the library. Something like stb_truetype is easy to swap out if the need arises. However, if I use SDL_GPU everywhere and don&#8217;t even make any graphics API abstractions, then I am heavily dependent on it.  The day I want a platform where I can&#8217;t use SDL_GPU, then I&#8217;m in deep trouble.</p><p>So I only use libraries that I&#8217;m forced to use (OS APIs, rendering APIs) and libraries that I can easily swap out (stb_truetype).</p></blockquote><p>Also, I ended up watching this informative and soothing video by Sebastian Lague. Perhaps trying to do some curve-based approach to text rendering is interesting, but as you can see in the video, it is a huge can of worms. We shall see!</p><div id="youtube2-SO83KQuuZvg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;SO83KQuuZvg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/SO83KQuuZvg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3>Karl2D API documentation</h3><p>Creating APIs that are easily overviewable isn&#8217;t one of Odin&#8217;s strengths. In C you use headers for that. But I don&#8217;t want any header-like things! But I also don&#8217;t want to force people to use a web-based documentation file.</p><p>What I&#8217;ve come up with instead is to use <code>core:odin/parser</code> to parse the source files and create a <code>karl2d.doc.odin</code> file that contains a good, programmer friendly overview (essentially stripping out the procedure bodies). The file is not meant to be compiled (it has <code>#+build ignore</code> at the top). It&#8217;s there for documentation while at the same time being as close to real code as possible.</p><p>You can see the generated file here: <a href="https://github.com/karl-zylinski/karl2d/blob/master/karl2d.doc.odin">https://github.com/karl-zylinski/karl2d/blob/master/karl2d.doc.odin</a></p><p>The source of the program that creates the file is available in the <code>api_doc_builder </code>folder of the repository.</p><h2>Binding generator rewrite</h2><p>Recently, <a href="https://github.com/karl-zylinski/odin-c-bindgen">odin-c-bindgen</a> got a big update: We switched from using the clang executable to directly using libclang. This was due to great help from the contributor Xandaron.</p><p>What this means is that we use libclang to analyze the C headers. And from the information we get out of libclang we can generate the bindings. This is more tidy and less error-prone than using the clang executable, ouputting JSON and then parsing that JSON.</p><p>Since all that happened, I&#8217;ve been putting off doing some maintenance work on the generator. The code has gotten a bit messy! So&#8230; I am going to use the great work that Xandaron did as a reference, and do a &#8220;bindgen 2.0&#8221; rewrite.</p><p>My idea is to have a clearer separation between different &#8220;stages&#8221; of generation:</p><ul><li><p>Collect: Use libclang to build an intermediate representation of the C headers.</p></li><li><p>Process: Take any extra configuration options and use that to transform the collected representation. For example, this is where &#8220;bit setification&#8221; of enums would happen. The output of this stage is a &#8220;final representation&#8221;.</p></li><li><p>Output: Take the final representation and prettily output it as Odin files. An important aspect, in order to keep the messiness down, is to make sure that the outputter knows nothing about libclang. So the two stages before this one must prepare the data in an orderly fashion. One of the big issues with the old version was that clang things seeped into the output stage. This give me a hard hard time when trying to fix bugs, because there is clang-related things happening just about anywhere.</p></li></ul><blockquote><p>The one thing I like less about libclang vs parsing JSON from clang is that libclang uses &#8220;visitors&#8221; to iterate children. So for example, to get all the fields of a struct definition, you pass libclang a callback that it will call for each child of the struct.</p><p>This makes the code so much more messy. It&#8217;s must better to just iterate an array. So what I do in the rewrite is that I just use the visitor to make an array of children, return that array and then loop through it. The code then becomes much easier to read.</p></blockquote><h2>Join my Discord community</h2><p>If you want to chat about my book, Karl2D or the binding generator, then why don&#8217;t you join my Discord community? It&#8217;s a friendly place where you can also talk about your projects, be it gamedev- or Odin-related.</p><p>Here&#8217;s the invite: <a href="https://discord.gg/4FsHgtBmFK">https://discord.gg/4FsHgtBmFK</a></p><p>Until next time, happy programming!</p><p>/Karl</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading my newsletter! Subscribe to get notified when the next one comes out (once a month).</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[A raylib alternative written in Odin]]></title><description><![CDATA[Also: Trying Jai and a better odin-c-binding generator.]]></description><link>https://news.zylinski.se/p/a-raylib-alternative-written-in-odin</link><guid isPermaLink="false">https://news.zylinski.se/p/a-raylib-alternative-written-in-odin</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Mon, 01 Sep 2025 19:42:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/39e663fd-45df-4008-a4a3-124c25339f06_1151x809.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Summer is dying here in Sweden. I last wrote to you near the end of June. Then I went on vacation. Now two months have passed; August has just ended.</p><p>My vacation was four weeks long. I barely used computers at all during that time, except for playing Stardew Valley in splitscreen for very many hours. Coming back to the computer I felt strangely curious about some old projects.</p><p>During my last newsletter I did some philosophizing about game engines. The newsletter included a link to a repository with some sketches on a plugin-based architecture for an Odin engine.</p><p>I haven&#8217;t done much more on that specific project, due to it feeling too unbounded, to unconstrained. Instead, what has happened is this: When I came back from the vacation I opened up my old &#8220;Metroidvaniaish&#8221; project. It&#8217;s a prototype I worked on last year that looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W-cO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W-cO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 424w, https://substackcdn.com/image/fetch/$s_!W-cO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 848w, https://substackcdn.com/image/fetch/$s_!W-cO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 1272w, https://substackcdn.com/image/fetch/$s_!W-cO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W-cO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png" width="1456" height="853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:853,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:62814,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/172506606?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!W-cO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 424w, https://substackcdn.com/image/fetch/$s_!W-cO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 848w, https://substackcdn.com/image/fetch/$s_!W-cO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 1272w, https://substackcdn.com/image/fetch/$s_!W-cO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dac6170-1d06-4634-bef7-cb5ee15128dd_1923x1126.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I dusted it off and easily got it running, thanks to Odin not having changed much. However, after that something else happened. I started thinking: What if I want to work on this project again; how would I want to do go about it? </p><p>The project used Raylib. While I managed to ship CAT &amp; ONION using Raylib, I thought it would also be nice to have something &#8220;native&#8221; written in Odin that I also have more ownership of.</p><p>With that in mind I started making an Odin wrapper for Raylib. The long-term goal being to replace the Raylib parts (that are written in C), with native Odin code. The project-dusting-off followed by the start of this wrapping can be seen in this stream:</p><div id="youtube2-oTJqTMlrJRM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;oTJqTMlrJRM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/oTJqTMlrJRM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Since then I have worked a lot on it. My aim is this:</p><ul><li><p>Make a native, Raylib-like library for creating video games.</p></li><li><p>The library doesn&#8217;t have to be identical to Raylib, the initial API is just inspired by Raylib because I like it. But I can also correct things that I don&#8217;t like about Raylib. I know the library very well, even how the internal C code works.</p></li><li><p>Use as little dependencies as possible. Use the &#8220;most native&#8221; APIs on each platform. Currently I&#8217;m working on a Windows version that uses win32 for windowing and d3d11 for rendering. I will not use GLFW, SDL, Sokol or any abstract thing like that. Hopefully I can get very far with just some <code>core</code> libraries and a few rendering libraries in <code>vendor</code>.</p></li></ul><p>One big technical difference from Raylib will thus be support for multiple rendering backends. A big benefit of this is that I&#8217;ll eventually be able to make a WebGL or WebGPU backend that has no need for emscripten. It&#8217;s quite exciting!</p><blockquote><p>Raylib uses OpenGL on all platforms. In order to make it work on web it uses emscripten to &#8220;translate&#8221; the OpenGL into WebGL. It&#8217;s a huge amount of magic that comes with many limitation and possible issues.</p></blockquote><p>I have given the library the name &#8220;karl2d&#8221;, and you can find the repository here: <a href="https://github.com/karl-zylinski/karl2d">https://github.com/karl-zylinski/karl2d</a> &#8212; <strong>Note that it is not ready for any kind of use and missing lots of features</strong>. At the moment of writing I&#8217;m working on making a proper &#8220;rendering backend&#8221; interface.</p><p>I have already gotten rid of the Raylib wrapper. Right now only a Win32/D3D version exists. Somewhere down the line I&#8217;ll hop into Linux and try to figure out the most suitable rendering backend and windowing solution.</p><p>My current biggest issue is choosing a shader language. I&#8217;m trying to figure out which shader infrastructure that comes with the least number dependencies. Remember: I want to minimize strange library dependencies. However, for shading compilation I may be forced to use some dependency.</p><p>I might use the new <em><a href="https://shader-slang.org/">slang</a></em><a href="https://shader-slang.org/"> language</a>. But then I cannot use OpenGL on Linux: I&#8217;ll be forced to use Vulkan. Vulkan is quite overkill and overcomplicated for this kind of thing. But I&#8217;ll do what makes most sense.</p><blockquote><p>If I make a Vulkan backend, then perhaps I&#8217;ll use it on Windows too, we&#8217;ll see. As noted, I still need multiple backends for web, mac and possible consoles.</p></blockquote><p>There are more shader language alternatives: I might use GLSL and some cross-compiler, I might use HLSL and some cross-compiler. I&#8217;m gonna try my best to find a low-friction pipeline.</p><p>Another alternative is to just use the format of whatever rendering API you run on right now and provide a separate &#8220;cross-compilation library&#8221;, so that the karl2d library stays simple. That&#8217;s good for actually shipping games, since you don&#8217;t have this weird cross compiler smack in the middle of it all. </p><p>We shall see how it goes. I&#8217;ll do more streams on YouTube where I work on the library.</p><h2>I recently tried Jai</h2><p>Just after the vacation ended, I did two live streams where I tried the Jai language, which is currently in closed beta. The two live streams are here:</p><div id="youtube2-Qx2-2IZVIN4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;Qx2-2IZVIN4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/Qx2-2IZVIN4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div id="youtube2-DXMSnMBW3xA" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;DXMSnMBW3xA&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/DXMSnMBW3xA?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>People naturally want me to provide comparisons of Jai and Odin. Pros and cons. Which is best? etc</p><p>I haven&#8217;t used Jai enough to say anything definite. What I can say is this:</p><ul><li><p>Jai works. I had no trouble with compiling my first program.</p></li><li><p>I was able to make a small 2D game thingy quite easily: <a href="https://github.com/karl-zylinski/learning-jai-by-making-a-game">https://github.com/karl-zylinski/learning-jai-by-making-a-game</a></p></li><li><p>The package system in Jai is more relaxed than Odin. This may or may not be a good thing. You can do a bit more whatever you want and load separate files into a package. It is not as bound to a specific folder as Odin is.</p></li><li><p>I didn&#8217;t really try any of the famous Jai meta-programming, because I don&#8217;t find meta-programming that interesting. When I need meta-programming in Odin I just generate .odin files. That has filled all my needs so far.</p></li></ul><p>I&#8217;m not planning on doing any more Jai in the near future. It seems like an OK language. But it is less polished than Odin and none of the additional features interest me that much.</p><h2>odin-c-bindgen: A big update</h2><p>The odin-c-bindgen, which generates Odin bindings for C libraries, now uses libclang instead of the clang executable. This means that it no longer has to write a JSON file to disk and read it back. Instead it just uses libclang to process the C headers, directly in the code.</p><p>Almost all this work was done by Xandaron, who I now see as &#8220;co-author&#8221; of the program, because he has done so much work on it. Huge thanks! You can read more about the release here: <a href="https://github.com/karl-zylinski/odin-c-bindgen/releases/tag/1.1">https://github.com/karl-zylinski/odin-c-bindgen/releases/tag/1.1</a></p><h2>Odin gamedev architecture</h2><p>I&#8217;m thinking of making a video or blog that is a bit more in-depth on how to do the architecture of a video game in Odin. It&#8217;s a huge topic where I need to limit myself. So I&#8217;m thinking: &#8220;Architecture for small, 2D, single-threaded games, probably using Raylib for simplicity&#8221;. If you have anything you&#8217;d like to see in such a video or blog, then let me know in the comments.</p><h2>Thanks for reading</h2><p>Have a great September! If you enjoyed this newsletter, then perhaps you want to subscribe? You&#8217;ll get roughly one newsletter, similar to this one, per month.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe by entering your e-mail below:</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Have a nice day!</p><p>/Karl Zylinski</p>]]></content:encoded></item><item><title><![CDATA[Vacation time! But first: Game engines...]]></title><description><![CDATA[I'm going on vacation for a month. But lets first talk a bit about what I think is important in game engines.]]></description><link>https://news.zylinski.se/p/vacation-time-but-first-game-engines</link><guid isPermaLink="false">https://news.zylinski.se/p/vacation-time-but-first-game-engines</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Fri, 27 Jun 2025 22:47:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9d99599b-fe85-4be4-8404-621d6d195022_1780x1440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Vacation time! I&#8217;ll be off during all of July. But before that, let&#8217;s talk a bit about what I&#8217;ve been tinkering with in June. Let&#8217;s talk a bit about game engines.</p><h2>Make your game!</h2><p>I tend to say that if you want to make a game from scratch (not using a any existing game engine), then you need to focus on making the game.</p><p>Do not try to first make a general purpose engine and then, when you are done with that engine, make your game on top.</p><p>Only solve the problem you actually have. Don&#8217;t dream about &#8220;what if I need X in the future&#8221;. Only write the code that gets you closer to shipping the game.</p><blockquote><p>If you are on a bigger team, then you may need to think a bit more about &#8220;what if we need X in the future&#8221;. The smaller the team, the more you are usually able to focus on The Now. In small teams its easier to do crazy restructuring of the code. The more people, the scarier it is to break things.</p></blockquote><h2>But I want to make an engine!</h2><p>That&#8217;s fine! Make an engine. You just need to be honest with yourself: Are you making a game, or making an engine?</p><blockquote><p>When I say engine in this newsletter, I mean &#8220;general purpose game engine&#8221;, something like Unity or Godot. If you make 3 games and some kind of engine fall out as a bi-product over time, then that&#8217;s not really what I refer to in this case.</p></blockquote><p>One thing you should keep in mind, is that that making an engine is a huge task. Let&#8217;s say that you want to make a general purpose game engine that other people can use. What is biggest amount of work? Fancy rendering code? An entity system? No: Tools. If you want to make an engine then you must be prepared to spend a lot of your time making tools. You must care about the user experience of those tools.</p><p>This may sound obvious, but here is a funny thing I&#8217;ve noticed with people who make game engines as a hobby: Many start with some 3D rendering code, perhaps they make some Physically Based Rendering pipeline, etc etc. And then when they&#8217;ve spent months on that, then they suddenly realize they need to make some tools. Many give it a go, don&#8217;t find it fun and then the project dies.</p><p>So here&#8217;s an idea: If you want to make a game engine, then you should probably start with these things:</p><ul><li><p>UI rendering</p></li><li><p>Some way to store / load data</p></li></ul><p>Then make some tiny toy tool using the UI rendering and go on from there.</p><p>Eventually, you can add some basic 2D / 3D rendering and experiment with some ideas of how to edit levels. Over time, you can add more rendering and perhaps an entity concept etc.</p><p>This way you&#8217;ll have a more holistic approach to game engine development. You&#8217;ve already started with the most important part: The tools.</p><h2>So why are you writing about this?</h2><p>I&#8217;m an experienced tools and engine programmer. So I thought it would be interesting to try the approach above. More specifically, what I would like is:</p><ul><li><p>A general purpose game engine written in Odin</p></li><li><p>Odin is also used for gameplay programming</p></li><li><p>I focus on getting the tools programming up to speed first. If I can&#8217;t get anywhere with the tools, then I might just as well not continue.</p></li><li><p>Plugin-based: Make the engine modular. Have hot reloadable plugins.</p></li><li><p>Low-level: Does not use any graphics API wrapper. Instead I want to use the &#8220;most native&#8221; graphics API for each platform. On Windows that&#8217;d be Direct3D. On Mac Metal. On Linux&#8230;. Vulkan I guess.</p></li></ul><p>I haven&#8217;t gotten far yet. But I&#8217;ve spent some time in June tinkering with these things.</p><p>I&#8217;ve written some rendering code in Direct3D 12 that can draw some rectangles in a single draw call. This will be the foundation of the Immediate Mode UI.</p><p>Then I&#8217;ve gotten started on the plugin system. It&#8217;s not 100% working yet, but I have my Direct3D 12 code as a plugin that is loaded dynamically.</p><h2>Thinking about code organization</h2><p>When making something modular and plugin-based, where the plugins are possible to dynamically load and hot reload, then each plugin probably becomes a separate package.</p><p>Using many small packages in Odin is usually a problem. You&#8217;ll compartmentalize incorrectly and you can&#8217;t have circular dependencies.</p><p>However, I envision my plugins to be quite big. At least the plugins of the core engine and the tools. The whole D3D12 renderer is one plugin etc. The IMUI rendering code is probably another plugin. A level editor may be another plugin.</p><p>So what I do is the following: In the D3D12 plugin I introduce an `@api` attribute. I mark procedures with it like so:</p><pre><code>@api
buffer_create :: proc(blabla) -&gt; Buffer_Handle {
&#9;// do things
}</code></pre><p>When I compile the plugin I use the parser in `core:odin` to find those procs marked with `@api`. I add their signature to an API struct. That&#8217;s the API the user of the plugin has to use.</p><p>The API file for the D3D12 plugin currently looks like so:</p><pre><code>package renderer_d3d12

import hm "kzg:base/handle_map"
import "kzg:base"

Shader_Handle :: distinct hm.Handle
Buffer_Handle :: distinct hm.Handle
Swapchain_Handle :: distinct hm.Handle
Pipeline_Handle :: distinct hm.Handle

State :: struct {}
Command_List :: struct {}

API :: struct {
&#9;create: proc(allocator := context.allocator, loc := #caller_location) -&gt; ^State,
&#9;destroy: proc(s: ^State),
&#9;create_swapchain: proc(s: ^State, hwnd: u64, width: int, height: int) -&gt; Swapchain_Handle,
&#9;destroy_swapchain: proc(s: ^State, sh: Swapchain_Handle),
&#9;create_pipeline: proc(s: ^State, shader_handle: Shader_Handle) -&gt; Pipeline_Handle,
&#9;destroy_pipeline: proc(rs: ^State, ph: Pipeline_Handle),
&#9;flush: proc(s: ^State, sh: Swapchain_Handle),
&#9;begin_frame: proc(s: ^State, sh: Swapchain_Handle),
&#9;draw: proc(s: ^State, cmd: ^Command_List, index_buffer: Buffer_Handle, n: int),
&#9;create_command_list: proc(s: ^State, ph: Pipeline_Handle, sh: Swapchain_Handle) -&gt; ^Command_List,
&#9;destroy_command_list: proc(rs: ^State, cmd: ^Command_List),
&#9;set_buffer: proc(rs: ^State, ph: Pipeline_Handle, name: string, h: Buffer_Handle),
&#9;begin_render_pass: proc(s: ^State, cmd: ^Command_List),
&#9;execute_command_list: proc(s: ^State, cmd: ^Command_List),
&#9;present: proc(s: ^State, sh: Swapchain_Handle),
&#9;shader_create: proc(s: ^State, shader_source: string) -&gt; Shader_Handle,
&#9;shader_destroy: proc(s: ^State, h: Shader_Handle),
&#9;buffer_create: proc(s: ^State, num_elements: int, element_size: int) -&gt; Buffer_Handle,
&#9;buffer_destroy: proc(s: ^State, h: Buffer_Handle),
&#9;buffer_map: proc(s: ^State, h: Buffer_Handle) -&gt; rawptr,
&#9;buffer_unmap: proc(s: ^State, h: Buffer_Handle),
&#9;swapchain_size: proc(s: ^State, sh: Swapchain_Handle) -&gt; base.Vec2i,
}</code></pre><p>The procs inside that API struct are matched up with the actual procs in the DLL when the plugin is loaded.</p><p>So the plugin is, from the viewpoint of the running game engine just two things: This API definition file and the DLL that contains the code. In order to avoid circular dependencies the API file should only import things from `core`, `base` and my own `kzg:base` package.</p><p>However, inside the code in the DLL, behind the &#8220;API wall&#8221;, it can import any other plugin. That&#8217;s fine, it&#8217;s not going to create a circular dependency from the viewpoint of the Odin compiler. So this way we get simple APIs and arbitrarily complicated code behind those APIs.</p><h2>The code</h2><p>You can look at the code here: <a href="https://github.com/karl-zylinski/kzg">https://github.com/karl-zylinski/kzg</a> &#8212; Keep in mind that currently it just renders some rectangles and does some plugin stuff. But it&#8217;s going to be fun to the the plugin stuff a bit more going and then go back and try to get the editor things off the ground.</p><h2>Well, I&#8217;m off to vacation for a month!</h2><p>Thanks for reading! Have a nice July!</p><p>/Karl</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to recieve the next newsletter:</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[May: Hacker news, marketing and ideas]]></title><description><![CDATA[May has ended, June is here. What happened in May?]]></description><link>https://news.zylinski.se/p/may-hacker-news-marketing-and-ideas</link><guid isPermaLink="false">https://news.zylinski.se/p/may-hacker-news-marketing-and-ideas</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Tue, 03 Jun 2025 12:49:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7c78ac7b-fa89-481e-91c6-22b0302b4668_1546x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>May has ended, June is here. What happened in May?</p><h2>A blog post that blew up on Hacker News</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://zylinski.se/posts/a-programming-language-for-me/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ea1F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 424w, https://substackcdn.com/image/fetch/$s_!Ea1F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 848w, https://substackcdn.com/image/fetch/$s_!Ea1F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 1272w, https://substackcdn.com/image/fetch/$s_!Ea1F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ea1F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png" width="1456" height="836" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:836,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:694213,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://zylinski.se/posts/a-programming-language-for-me/&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/165089816?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ea1F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 424w, https://substackcdn.com/image/fetch/$s_!Ea1F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 848w, https://substackcdn.com/image/fetch/$s_!Ea1F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 1272w, https://substackcdn.com/image/fetch/$s_!Ea1F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F672b8b7a-2d1e-4316-bc28-661a6f883650_3089x1774.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I wrote <a href="https://zylinski.se/posts/a-programming-language-for-me/">this blog post</a> in which I shared my personal story of how I used to program in C and how I stumbled onto Odin, and how it fit my specific needs.</p><p>It wrote it partially because my book says &#8220;Odin point-for-point addresses many of the issues I've had with C, while also incorporating some of my favorite C best practices, straight into the language&#8221;. But it doesn&#8217;t elaborate much more than that. So the blog post fills in that blank!</p><p><a href="https://news.ycombinator.com/item?id=43970800">It ended up Hacker News</a>, on the front page! It is fun to see that Odin-related things tend to find their way to Hacker News so often.</p><h2>Organic and paid marketing</h2><p>Let&#8217;s talk a bit about marketing! The best kind of marketing is called &#8220;organic&#8221;. It means that people are excited enough about your product that they talk about it for you. They repost your social media posts, etc.</p><p>Even better is when the content wasn&#8217;t even created to be marketing. Perhaps you were just excited to share something. But it blew up. These kind of things can happen when one least expects it. It&#8217;s hard to &#8220;make it happen&#8221;. Because then you&#8217;re kind of &#8220;faking it&#8221;. Just make sure you&#8217;re making something you&#8217;re honestly excited about, and share interesting things about it!</p><p>I have never tried paid marketing before, until last month. During May I tried running ads for <a href="https://odinbook.com/">my book</a> on Reddit. My idea was this: I put in $10 of ads per day. If I manage to sell 1 extra book per day then I&#8217;ve made $10 (since the book cost roughly $20). It felt like a good, non-risky way to approach it.</p><p>Did it work? I&#8217;m not 100% sure, since the Hacker News stuff happened at roughly the same time. But it seems like I&#8217;ve sold 1-2 more copies per day, even after the Hacker News exposure died down.</p><p>Why Reddit? Because it&#8217;s so easy to specifically target niche programming subreddits. That way I could get the maximum value for my daily $10. You can of course do targeted ads on other platforms as well, but it just feels easier to set up on Reddit. You can just choose to target some subreddit that you know you enjoy yourself.</p><p>Note: I did not target the <a href="https://www.reddit.com/r/odinlang/">Odinlang subreddit</a>, since I&#8217;m the moderator of that one! Running ads on platforms where you&#8217;re supposed to be a neutral moderator may not look very good.</p><h2>Small book update</h2><p>Just before writing this, I also released a small book update. You can <a href="https://zylinski.itch.io/odinbook/devlog/959059/version-17-minor-fixes-and-the-future">read the change log on itch</a>. It&#8217;s nothing big, just a few typo fixes.</p><h2>What happens now?</h2><p>I wish I knew!</p><p>I&#8217;ve been trying to make a new game. It hasn&#8217;t gone very well. The book money is coming in, but I don&#8217;t think I&#8217;ll make enough to last me more than a few months. Doing these things is my main income.</p><p>I&#8217;ll still poke around with some projects and see if anything interesting pops up. But I do feel a bit deflated and stressed. As you may have noted, I&#8217;ve made less videos and blog posts than usual.</p><p>I&#8217;m sure I&#8217;ll get through it, but perhaps some bigger change will be needed soon. We&#8217;ll see! I&#8217;ll try to figure these things out during June. And then have a proper summer vacation in July.</p><p>Have a nice day!</p><p>/Karl Zylinski</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe to get future newsletters into your inbox.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[April: Arenas, no-engine gamedev and some reflections]]></title><description><![CDATA[Memory arenas. Updates on my open-source projects. No-engine gamedev. Some personal reflections on Sokol and Raylib.]]></description><link>https://news.zylinski.se/p/april-arenas-no-engine-gamedev-and</link><guid isPermaLink="false">https://news.zylinski.se/p/april-arenas-no-engine-gamedev-and</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Wed, 30 Apr 2025 23:21:32 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c98cf9ae-7655-4919-8905-fd7bf972face_2000x1440.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>April! What happened? I did a lot of blogging about Odin-related things.</p><p>I wanted to give people some insights on memory arenas. So wrote about that. Then there was a bunch of blogging related to libraries and software I&#8217;ve made. I summarize it all below.</p><p>At the end of this newsletter I also show a sneak peek of my latest gamedev experiments, along with some reflections on Odin, Sokol and Raylib.</p><h2>Memory arenas and handle-based maps</h2><p>A thing I often see is people trying to combine arena allocators with dynamic arrays. You can sometimes do that, but you need to be aware of a bunch of potential pitfalls. There are also a bunch of cool tricks you can use to your benefit. With that in mind I wrote <a href="https://zylinski.se/posts/dynamic-arrays-and-arenas/">a blog post</a> and also made a video on the topic:</p><div id="youtube2-1WnqZPD-qVc" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;1WnqZPD-qVc&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/1WnqZPD-qVc?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Some of the ideas in that blog post actually made me re-design my &#8220;handle-based map&#8221; package.</p><blockquote><p>A handle-based map is used to associate array items with robust identifiers. The handle is essentially an index plus a generation counter.</p></blockquote><p>In particular, I made that package use virtual memory arenas in smarter ways. I wrote <a href="https://zylinski.se/posts/handle-based-maps-three-implementations/">a whole post</a> about that as well! In it I explain three different approaches of implementing a handle-based map. The updated source code can be found <a href="https://github.com/karl-zylinski/odin-handle-map">on GitHub</a>.</p><p>I made a video about this as well:</p><div id="youtube2-MzR1us2nZPY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;MzR1us2nZPY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/MzR1us2nZPY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>I&#8217;ve chosen to write about handle-based maps because it&#8217;s something that many game developers run into. But there are lots of small nuances related to how one implements it. When making CAT &amp; ONION I rewrote my handle-based map several times and figured things out as I went. I hope I can save people some of that time.</p><h2>My Odin C Binding generator</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://zylinski.se/posts/generate-odin-bindings-for-c-libraries/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EqUW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 424w, https://substackcdn.com/image/fetch/$s_!EqUW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 848w, https://substackcdn.com/image/fetch/$s_!EqUW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!EqUW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EqUW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png" width="1456" height="846" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:846,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:136263,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://zylinski.se/posts/generate-odin-bindings-for-c-libraries/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/162484055?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!EqUW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 424w, https://substackcdn.com/image/fetch/$s_!EqUW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 848w, https://substackcdn.com/image/fetch/$s_!EqUW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 1272w, https://substackcdn.com/image/fetch/$s_!EqUW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3041d9ef-2cd1-4acb-8b31-d7c96f4ea2f7_2044x1188.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At the start of April I wrote a <a href="https://zylinski.se/posts/generate-odin-bindings-for-c-libraries/">blog post</a> that explains how to use my <a href="https://github.com/karl-zylinski/odin-c-bindgen">Odin C binding generator</a> and what the benefits of a generator can be.</p><p>This binding generator takes C headers and tries to output Odin bindings that utilize Odin features such as <code>bit_set</code>. It also tries to generate bindings that actually <em>look good </em>and are easy on the eye. I think that bindings should be as easy to learn from as the original headers. With that in mind, it also makes sure to bring along comments and documentation.</p><p>If you try to use it and run into any problem, then please post an issue <a href="https://github.com/karl-zylinski/odin-c-bindgen">on GitHub</a>! I&#8217;ve also recieved a bunch of Pull Requests that have added features and fixed bugs. Thanks a lot to those who helped me!</p><h2>No-engine gamedev using Odin + Raylib</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://zylinski.se/posts/no-engine-gamedev-using-odin-and-raylib/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lapv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!lapv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!lapv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!lapv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lapv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf88f672-beed-426e-8405-3097af85688c_2560x1440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:549860,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://zylinski.se/posts/no-engine-gamedev-using-odin-and-raylib/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/162484055?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lapv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!lapv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!lapv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!lapv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf88f672-beed-426e-8405-3097af85688c_2560x1440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I realize that some people may find &#8220;making games from scratch&#8221; daunting. By &#8220;from scratch&#8221; I mean not using any big engine.</p><p>Exactly what &#8220;from scratch&#8221; means varies from person to person. Some may say that it means building everything on top of Operating System APIs and rendering APIs such as Vulkan. To me it just means that you use a programing language and some kind of basic library, such as Odin + Raylib. But you don&#8217;t have any big engine such as Unity or Godot.</p><p>Recently, I&#8217;ve also tried to use the term &#8220;no-engine gamedev&#8221;. But that also confuses some. Then people debate about what an engine is instead! By engine, I again mean a big engine like Unity. So &#8220;no-engine gamdev&#8221; means making a game without such software.</p><p>In any case, I wrote a blog post called &#8220;<a href="https://zylinski.se/posts/no-engine-gamedev-using-odin-and-raylib/">No-engine gamedev using Odin + Raylib</a>&#8221; that attempts to be a quick crash course on the super basics of Odin + Raylib. Those basics are followed up by some pointers on how to manage entities and how I approach making level editors. I hope it can make the idea of no-engine gamedev less daunting. The post is almost like a little &#8220;summary post&#8221; of all the different things I&#8217;ve done in this area, with links to videos, blog posts and articles where you can learn more about specific things.</p><h2>The Zylinskiverse</h2><p>I&#8217;ve made it so you can buy my Odin book on the domain <a href="https://store.zylinski.se/b/tnwvO">store.zylinski.se</a> &#8212; My own store, so profesh! I&#8217;ve also made it so that this newsletter is hosted on <a href="https://news.zylinski.se/">news.zylinski.se</a>. Welcome to the Zylinskiverse!</p><p>In the future, perhaps I&#8217;ll also start selling CAT &amp; ONION on my store.</p><h2>Some thoughts on Sokol and Raylib</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NdBR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NdBR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 424w, https://substackcdn.com/image/fetch/$s_!NdBR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 848w, https://substackcdn.com/image/fetch/$s_!NdBR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 1272w, https://substackcdn.com/image/fetch/$s_!NdBR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NdBR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png" width="1456" height="627" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:627,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1583104,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://news.zylinski.se/i/162484055?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NdBR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 424w, https://substackcdn.com/image/fetch/$s_!NdBR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 848w, https://substackcdn.com/image/fetch/$s_!NdBR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 1272w, https://substackcdn.com/image/fetch/$s_!NdBR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39da1c21-a669-49cf-b652-2a4e937e0639_1803x777.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The last few days me and my partner have been poking around with some little video game experiments. I don&#8217;t have much to show other than the strange image above.</p><p>But I do have some technical notes to share. Initially, I started doing it in Sokol, using my <a href="https://github.com/karl-zylinski/odin-sokol-hot-reload-template">sokol-hot-reload-template</a>. I was trying to do a 3D game with flat, paper-like characters and trees and stuff.</p><p>I went with Sokol because I did have some <a href="https://github.com/karl-zylinski/raylib-first-person-experiments">trouble using 3D in Raylib before</a>. In what way? The 2D support in Raylib is great. But the 3D support often forces you to go into the C code of Raylib and port some functions over to Odin, so that you can modify them. This is because Raylib&#8217;s 3D code makes a bit too many assumptions about how you want to set things up. For example, I didn&#8217;t like how `rl.DrawMeshInstanced()` worked internally. So <a href="https://github.com/karl-zylinski/raylib-first-person-experiments/blob/7a13984ccb953873e2373212ae737fa68bcc62dd/game.odin#L737">I ported it to Odin</a> and adapted it to my own needs.</p><p>Initially everything went great with Sokol! I got shadowmapping working and managed to create flat, paper-like characters that used my partner&#8217;s art. Sokol is more low-level than Raylib (it&#8217;s more like a rendering API and less like a &#8220;game creation library&#8221;), so none of the annoying assumptions bit me.</p><p>But then I wanted to make an editor. For CAT &amp; ONION I had a very fun time making editors using Raylib&#8217;s 2D drawing features. A button was just a rectangle with some text in it. I got a lot done, very quickly! Without any extra IMGUI library or anything.</p><p>Sokol didn&#8217;t have such things built in. Naturally. It&#8217;s more low-level! Less assumptions! But I also didn&#8217;t want to pull in Dear IMGUI or something like that. So I started making my own &#8220;rectangle drawer&#8221; procs, similar to <code>rl.DrawRectangle</code> etc. Quite quickly I was totally lost in the sauce of making utility procs, when what I really wanted to do was to make my editor. I wanted to just quickly layout some panels with some buttons on them to spawn some graphics!</p><blockquote><p>People may scream at me &#8220;well, then, just put in Dear IMGUI or something!!&#8221;. But I don&#8217;t want to! I like writing my own IMGUI stuff, because I come from a tools programming background. I just don&#8217;t want to write the code that draws the rectangles and a million different variations of such procedures.</p></blockquote><p>Also, I was actually missing some of the basic collision detection features of Raylib. Not that it&#8217;s very hard to implement them. But I was just starting to feel tired from having to implement so many small things. I got side-tracked every time I tried to implement something for the game.</p><p>At this point it dawned on me: I wasn&#8217;t having fun. I love when the level at which you&#8217;re programming is such that you feel like you can easily do the part that is &#8220;most fun&#8221; to you. For some people perhaps the graphics programming is most fun. But for me the most fun part is bolting together gameplay, shaders, making editors, thinking about data formats. Making it all work together. But I also love the &#8220;from scratch&#8221; feeling of not using a big game engine. I love when my game is just a program that uses a library. I want it to be as simple as possible, but still give me a suitable amount of features.</p><p>In any case, since I wasn&#8217;t having fun and just wanted all my good old Raylib stuff, I just re-wrote it all using Raylib. I know there may be some issues down the road regarding the assumptions in Raylib 3D, but I think I can work around them. It&#8217;ll be OK!</p><p>By all this, I don&#8217;t mean to tell anyone what libraries to use. The important thing here is that you find something that makes it possible for you to quickly get to the parts that are most fun <em>for you</em>. Find that sweet spot!</p><h2>Thanks for reading!</h2><p>If you want to hang out and discuss Odin or game development, then join my Discord server: <a href="https://discord.gg/4FsHgtBmFK">https://discord.gg/4FsHgtBmFK</a> &#8212; It&#8217;s a friendly place!</p><p>/Karl Zylinski</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Sign up below to get future newsletters. Once a month!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[March 2025: Book update, game jam, new libraries... and the future!]]></title><description><![CDATA[Game jam, book update, new libraries and templates!]]></description><link>https://news.zylinski.se/p/march-2025</link><guid isPermaLink="false">https://news.zylinski.se/p/march-2025</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Sat, 29 Mar 2025 16:54:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6411858b-fe35-4a30-8159-bb2e39075332_2207x1440.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi! Welcome to my first-ever newsletter. I'm happy that so many have signed up. Let's look at what happened in March!</p><h2>Updates to my book and the future of it</h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://zylinski.itch.io/odinbook/devlog/909629/version-16-keeping-the-book-up-to-date" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jOd8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jOd8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jOd8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jOd8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jOd8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg" width="1210" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79976,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:&quot;https://zylinski.itch.io/odinbook/devlog/909629/version-16-keeping-the-book-up-to-date&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://karlzylinski.substack.com/i/160137936?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jOd8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jOd8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jOd8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jOd8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f472394-b128-48e3-96b6-77318166cede_1210x300.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>My book <a href="https://odinbook.com/">"Understanding the Odin Programming Language"</a> got a 1.6 update. It was mostly about updating a few things that had changed in the language. I also added some new sections, such as how to break cyclic package imports using callbacks and interfaces. You can read the whole change log here: <a href="https://zylinski.itch.io/odinbook/devlog/909629/version-16-keeping-the-book-up-to-date">https://zylinski.itch.io/odinbook/devlog/909629/version-16-keeping-the-book-up-to-date</a></p><p>I also want to talk about the future of the book.</p><p>My aim is to keep it up-to-date. This means that I'll update it if important things change in the language. These updates will be free for those who have already bought the book, just like past updates.</p><p>I have also decided to <em>not</em> make a physical version of the book. Sorry anyone who was waiting for that! My reasoning is like follows: When I wrote the book I wanted to create a semi-interactive work. It does explain many things in great detail, but I also don't shy away from linking to articles, videos, documentation and code. I do it in a kind of informal way, without footnotes. Changing all that for a physical version is <em>a lot</em> of work. Frankly I just don't think it'll work very well in physical format. A second reason is that I just didn't find the energy to work on the physical version. So I just won't.</p><p>A huge thanks to everyone who has bought the book and said such nice things about it. It's my first independent work that has sold well enough in order to sustain me for a while. Everything I currently do is funded by book sales.</p><h2>I told my programming life story on a podcast</h2><p>I was a guest on the Wookash Podcast (previously known as the "Game Engineering Podcast"). You can listen / watch here:</p><div id="youtube2-W2rwB1LTSF8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;W2rwB1LTSF8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/W2rwB1LTSF8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>It was very fun! The front part of it ended up being me talking about all the twists and turns that can lead to one's current occupation. The second half is more specific about game development and Odin. Thanks to &#321;ukasz for hosting and having me on!</p><h2>Handle-based map blog posts and library</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://zylinski.se/posts/handle-based-arrays/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4clS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 424w, https://substackcdn.com/image/fetch/$s_!4clS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 848w, https://substackcdn.com/image/fetch/$s_!4clS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!4clS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4clS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1988993,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:&quot;https://zylinski.se/posts/handle-based-arrays/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://karlzylinski.substack.com/i/160137936?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4clS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 424w, https://substackcdn.com/image/fetch/$s_!4clS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 848w, https://substackcdn.com/image/fetch/$s_!4clS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!4clS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F035193e0-27f9-4b08-ae3b-8fbff326c70a_2560x1440.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It's often important to store references to objects within your code. However, many of us have probably had problems with dangling pointers etc.</p><p>A common remedy is to store <em>handles</em> instead of pointers. A few days ago I made a blog post about that: <a href="https://zylinski.se/posts/handle-based-arrays/">https://zylinski.se/posts/handle-based-arrays/</a></p><p>Towards the end of the blog post I talked about some further ideas regarding how to use virtual memory arenas in order to make the code more robust. My curiosity resulted in the following <strong>odin-handle-map</strong> library: <a href="https://github.com/karl-zylinski/odin-handle-map">https://github.com/karl-zylinski/odin-handle-map</a></p><blockquote><p><strong>Note:</strong> I say handle-based array, handle-based map and handle map interchangeably.</p></blockquote><p>In many ways my blog post was meant as a follow-up to Andre Weissflog's 2018 article named <a href="https://floooh.github.io/2018/06/17/handles-vs-pointers.html">"Handles are the better pointers"</a>.</p><h2>I made a Sokol + Hot Reload template</h2><p>Speaking of Andre Weissflog. He's also the creator of <em>Sokol</em>. It is a library that, among other things, lets you talk to the GPU through an abstract interface. This means that you write the code once, but it will use Direct3D, OpenGL or Metal depending on the platform.</p><p>I'm well known for my <a href="https://github.com/karl-zylinski/odin-raylib-hot-reload-game-template">Odin + Raylib hot reload template</a>. But I also wanted hot reload capabilities when working with Sokol. So I created a template for that: <a href="https://github.com/karl-zylinski/odin-sokol-hot-reload-template">https://github.com/karl-zylinski/odin-sokol-hot-reload-template</a></p><p>When comparing this template to my Raylib Hot reload template, you may note a major difference: This template has a build script written in Python. So instead of several batch / bash scripts, there is a single Python script for all platforms and build modes. There is a thing that tipped the scale towards writing a Python script for this template: I wanted to automate the download and build of the Sokol bindings and libraries.</p><p>I was also considering writing the script in Odin itself, but since Odin has no bundled HTTP client yet, I decide to go with Python.</p><p>Here's a technical overview and demo of the template:</p><div id="youtube2-0wNjfgZlDyw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;0wNjfgZlDyw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/0wNjfgZlDyw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>I hosted the Odin 7 Day Jam</h2><p>The game jam I hosted March 8 - 15 was a big success! In the end we had 62 games submitted. The winner was decided by the participants voting. The result is here: <a href="https://itch.io/jam/odin-7-day-jam/results">https://itch.io/jam/odin-7-day-jam/results</a></p><p><strong>Let's talk about four of the games!</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://gordonshamway23.itch.io/pass-whales" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2APB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!2APB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!2APB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!2APB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2APB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png" width="315" height="250" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:250,&quot;width&quot;:315,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:38637,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://gordonshamway23.itch.io/pass-whales&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://karlzylinski.substack.com/i/160137936?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2APB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!2APB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!2APB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!2APB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c135f57-61b5-46f2-81b0-95b02eb85fcf_315x250.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The winner was "Pass Whales", a very nice and cozy little puzzle game: <a href="https://gordonshamway23.itch.io/pass-whales">https://gordonshamway23.itch.io/pass-whales</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://lucypero.itch.io/voidcrawl" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1Tzv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!1Tzv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!1Tzv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!1Tzv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1Tzv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png" width="315" height="250" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:250,&quot;width&quot;:315,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58929,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://lucypero.itch.io/voidcrawl&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://karlzylinski.substack.com/i/160137936?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1Tzv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!1Tzv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!1Tzv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!1Tzv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb75e0ef3-3f7f-49ff-9e34-df51dfa7ba34_315x250.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The second place was "Voidcrawl", which is a 3D dungeon crawler with an amazing atmosphere! <a href="https://lucypero.itch.io/voidcrawl">https://lucypero.itch.io/voidcrawl</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://tacohej.itch.io/slimeslash" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WcTy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!WcTy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!WcTy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!WcTy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WcTy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png" width="315" height="250" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:250,&quot;width&quot;:315,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://tacohej.itch.io/slimeslash&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://karlzylinski.substack.com/i/160137936?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WcTy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!WcTy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!WcTy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!WcTy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e494b20-3e27-42bf-8659-e9580dbeb580_315x250.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Third place was "Slice Slash", which was a cleverly designed puzzle game: <a href="https://tacohej.itch.io/slimeslash">https://tacohej.itch.io/slimeslash</a></p><p>Those three games are playable in the browser.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://blunderguru.itch.io/barrys-revenge" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ED9w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!ED9w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!ED9w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!ED9w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ED9w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png" width="315" height="250" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:250,&quot;width&quot;:315,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36944,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://blunderguru.itch.io/barrys-revenge&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://karlzylinski.substack.com/i/160137936?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ED9w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 424w, https://substackcdn.com/image/fetch/$s_!ED9w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 848w, https://substackcdn.com/image/fetch/$s_!ED9w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 1272w, https://substackcdn.com/image/fetch/$s_!ED9w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898564b-7c11-42ea-b9f5-7f9fbbb5788f_315x250.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>But I also want to mention another game: Barry's Revenge! It's a Hotline Miami-meets-"western movie" game. It's absolutely spectacular. It has nice graphics, tight and challenging gameplay and even voice acting. I was floored that it was made in 7 days! It didn't make the top 10 because it does not have a web build, so it didn't get enough votes. But I encourage you to check it out here: <a href="https://blunderguru.itch.io/barrys-revenge">https://blunderguru.itch.io/barrys-revenge</a></p><p>All-in-all, a huge thanks to everyone who participated! You're all an amazingly creative bunch.</p><h2>My future</h2><p>Personally, I had a bit of trouble making a game during the game jam. I think the pressure of hosting and being creative got to me. I might host jams in the future, but in that case I'd probably have a more &#8220;tutor role&#8221;: Answer questions on stream, show how to do specific game development things, hang out in Discord voice chat rooms and help people with their problems, etc! I'm actually quite excited about that idea; I really like teaching and helping.</p><p>But also, I've realized something else about my game making: I've tried to make another game for about a year and it hasn't gone that well. I ended up making a book instead, which was great fun. But the idea that I "should" make another game has been looming.</p><p>After the jam I came to a realization: I made CAT &amp; ONION as a solo developer. It a great experience and I learned a lot! But I actually don't think I should make games alone, I should have at least one more person to work with. The most important aspect for me is to have someone who is deeply involved in the game. That way we can collaborate on the game design.</p><p>So I won't try to make another game alone unless some <em>magical</em> inspiration comes around. But I might do it in a group. We'll see!</p><p>Other than that I'm excited to continue making educational content such as videos and blog posts as well as open source libraries and host game jams.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to future newsletters. You&#8217;ll get it roughly once a month.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Support me</h2><p>If you like the videos, blog posts and libraries that I make, then you can support me by:</p><ul><li><p>Buying my book: <a href="https://odinbook.com/">https://odinbook.com</a></p></li><li><p>Becoming a patron: <a href="https://www.patreon.com/karl_zylinski">https://www.patreon.com/karl_zylinski</a></p></li><li><p>Sponsoring me on GitHub: <a href="https://github.com/sponsors/karl-zylinski">https://github.com/sponsors/karl-zylinski</a></p></li><li><p>Buying my game CAT &amp; ONION: <a href="https://zylinski.itch.io/cat-and-onion">Itch</a> / <a href="https://store.steampowered.com/app/2781210/CAT__ONION/">Steam</a></p></li><li><p>Saying something nice on my Discord server: <a href="https://discord.com/invite/4FsHgtBmFK">https://discord.com/invite/4FsHgtBmFK</a></p></li></ul><p>Thanks for reading!</p><p>/Karl Zylinski<br>March 29, 2025</p>]]></content:encoded></item><item><title><![CDATA[New beginnings: A newsletter!]]></title><description><![CDATA[I'm starting a newsletter. It'll contain updates on projects, announcements of new projects and lots of interesting things about Odin and game development.]]></description><link>https://news.zylinski.se/p/new-beginnings-a-newsletter</link><guid isPermaLink="false">https://news.zylinski.se/p/new-beginnings-a-newsletter</guid><dc:creator><![CDATA[Karl Zylinski]]></dc:creator><pubDate>Fri, 21 Mar 2025 12:27:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a9ebe6a3-e62f-4eb4-b7d2-124cbe1f9107_1024x750.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi there!</p><p>Over the last few years I&#8217;ve been making a lot of different things related to programming and game development. I&#8217;ve written a book about the Odin Programming Language. I made my own video game, CAT &amp; ONION. I&#8217;ve run a YouTube channel, I&#8217;ve written blog posts, I&#8217;ve published open source software.</p><p>Yet, there has been no central place to get a digest of what I&#8217;m currently up to.</p><p>That&#8217;s why I&#8217;m starting this newsletter!</p><p>If you register, you&#8217;ll get roughly one e-mail per month. In it you&#8217;ll get updates about past projects, announcements of new projects and also links to any blog posts or videos I&#8217;ve made since the last newsletter. Some letters may also contain additional insights on programming or game development.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://news.zylinski.se/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Sign up here:</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Have a nice day!</p><p>/Karl Zylinski</p>]]></content:encoded></item></channel></rss>