<?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"><channel><title><![CDATA[Marco Alka]]></title><description><![CDATA[Software Engineer, mainly working as FullStack and DevOps developer at Robert Bosch GmbH. As a hobby, I also do Game Dev, Embedded IoT tinkering & I mentor @ ht]]></description><link>https://blog.marco-alka.de</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 07:56:54 GMT</lastBuildDate><atom:link href="https://blog.marco-alka.de/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I built a TypeScript Entity Component System]]></title><description><![CDATA[Programming simulations usually is no simple task. Especially when it's about creating a computer game, which has a lot of data to crunch and a lot of different logics to run. Organization is the most crucial part, and traditionally, every game studi...]]></description><link>https://blog.marco-alka.de/how-i-built-a-typescript-entity-component-system</link><guid isPermaLink="true">https://blog.marco-alka.de/how-i-built-a-typescript-entity-component-system</guid><category><![CDATA[ShowHashnode]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Marco Alka]]></dc:creator><pubDate>Mon, 20 Apr 2020 13:15:42 GMT</pubDate><content:encoded><![CDATA[<p>Programming simulations usually is no simple task. Especially when it&#39;s about creating a computer game, which has a lot of data to crunch and a lot of different logics to run. Organization is the most crucial part, and traditionally, every game studio has their own secret sauce. However, times are changing, we see computers with more and more cores, a flood of Indy Games, and a blossoming opensource world. All of that led to more uniform approaches, and also very elaborate systems to handle the problem. Lately, for example, one rather old idea is making the round and seeing implementations in different game engines, like <a target='_blank' rel='noopener noreferrer'  href="https://unity.com/dots">Unity (DOTS)</a>, <a target='_blank' rel='noopener noreferrer'  href="https://developer.apple.com/documentation/gameplaykit">GameplayKit</a>, <a target='_blank' rel='noopener noreferrer'  href="https://amethyst.rs/">Amethyst</a>, and many more. I am talking about Entity Component Systems. I created <a target='_blank' rel='noopener noreferrer'  href="https://www.npmjs.com/package/sim-ecs">my own library</a>, and here is why and how.</p>
<hr>
<h2 id="content">Content</h2>
<ul>
<li><a class='post-section-overview' href="#tl-dr">tl;dr</a></li>
<li><a class='post-section-overview' href="#what-is-an-ecs-">What is an ECS?</a></li>
<li><a class='post-section-overview' href="#why-create-this-project">Why create this project?</a></li>
<li><a class='post-section-overview' href="#from-inspiration-to-implementation">From inspiration to implementation</a></li>
<li><a class='post-section-overview' href="#about-challenges-and-hurdles">About challenges and hurdles</a></li>
<li><a class='post-section-overview' href="#what-s-next-">What&#39;s next?</a></li>
</ul>
<hr>
<h2 id="tl-dr">TL;DR</h2>
<p>Don&#39;t want to read all the details? Read this section for the minimal gist. If you are interested in the tech and want to learn something new, skip this chapter and read on about &quot;<a class='post-section-overview' href="#what-is-an-ecs-">What is an ECS?</a>!&quot;</p>
<p>I created a ECS in TypeScript, mainly because the existing projects are dead, missing features, PoCs, or I don&#39;t like how they solve certain problems. You can find my project here: <a target='_blank' rel='noopener noreferrer'  href="https://www.npmjs.com/package/sim-ecs">sim-ecs</a>. It&#39;s an ECS focused on fast iteration speed and ease-of-use. I was mainly inspired by <a target='_blank' rel='noopener noreferrer'  href="https://github.com/amethyst/specs">SPECS</a> and <a target='_blank' rel='noopener noreferrer'  href="https://github.com/TomGillen/legion">Legion</a>, two modern Rust ECS libraries, but handled some things differently and tailored for JS. Having a great ECS library for JS allows me to quickly build systems and PoC features I want to put into my game without spending a lot of time writing compile-able Rust code or introducing technical debt to my main code base.</p>
<p>If you are interested in SPECS and Legion, how they work, compare to each other and what kind of differences they have, I recommend <a target='_blank' rel='noopener noreferrer'  href="https://csherratt.github.io/blog/posts/specs-and-legion/">this great article</a> by Cora Sherratt, which compares them in detail.</p>
<h2 id="what-is-an-ecs-">What is an ECS?</h2>
<p>First of all it is important to understand, what an Entity Component System actually is and what it is used for. In an ECS, we have three different parts. Firstly, we have systems, which are blocks of logic, which operate over data sets. Secondly, there are components, which are blocks of data. And thirdly, there are entities, which act like glue for components, bundling them into packages of information. The clever thing about an ECS, however, is the way how these three parts work as a whole. An ECS can be thought of as a big database table. The columns of the table represent possible components, and the rows are component combinations, indexed by an entity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1587386836688/9Iyi3OJmm.png" alt=""></p>
<p>There are several advantages to having an ECS as the backbone of your simulation. The one, why the Rust simulation community is so into it is because it works very well with the ownership model of Rust, which, while eliminating a lot of runtime problems, is also difficult to work with (because the solved problems are difficult in the fist place...). We don&#39;t use Rust here, however having a good separation of data and logic, and doing composition of objects as opposed to inheriting classes, resulting in strange workarounds, is a boon no matter how you look at it. Additionally, when using an ECS, logic is automatically split up into easily parallelize-able chunks, so we can make use of a lot of CPU cores for free! Note: For JS, this means leveraging WebWorkers in the browser, or the Cluster module in NodeJS.</p>
<p>If you want to know more about why the ECS architecture is awesome, I recommend reading the <a target='_blank' rel='noopener noreferrer'  href="https://kyren.github.io/2018/09/14/rustconf-talk.html">RustConf 2018 Closing Keynote</a> by Kyren from Chucklefish. It is a lengthy read, but it&#39;s a good read!</p>
<h2 id="why-create-this-project-">Why create this project?</h2>
<p>One could argue that creating an ECS for JS is stupid. It would be a lot better to write a simulation with WASM as a target. Plus it is difficult to harness the power of several CPU cores from JS. Also, let&#39;s not forget that there probably already are a good number of libraries available solving the same problem. Why not just slap one of those into my applications?</p>
<p>While it is true that I used this project to learn more about ECSs, it certainly also is no excuse to that end when I say that the available ECS systems are not TS (I want type-support!), dead projects, PoCs themselves, or solve the problem in a way which I don&#39;t like and might require a lot of overhauling to implement features I want. For example, if I want to PoC a feature for my game, written in Rust with modern, fully featured ECS systems, I need ways to juggle worlds, components and entities, while still having solid performance (as much as you could expect from pure JS anyway). Ideally with a similar API and feature set, and the level of ease-of-use and DX I am used to from the Rust libraries. By the way, that&#39;s also the reason why I want an implementation for TS, instead of slapping something into the browser with WASM. WASM cannot handle many things about JS, like usage of Prototypes - which would result in a lot of number-workarounds or string shuffling, which also is not very performant to do between JS and WASM.</p>
<p>Since writing this ECS took a long time and lots of thinking out of the box, I also hope that I can help other game devs, who just want to create a small HTML5 game, but also want to have the full potential of big systems directly in their hands. Most folk who want to write a game do not want to learn a difficult language, but might already be familiar with webdev.</p>
<h2 id="from-inspiration-to-implementation">From inspiration to implementation</h2>
<p>Speaking of the big systems, I did not pull most of how sim-ecs looks like and works out of thin air. My two major inspirations are the modern Rust ECS libraries, <a target='_blank' rel='noopener noreferrer'  href="https://github.com/amethyst/specs">SPECS</a> and <a target='_blank' rel='noopener noreferrer'  href="https://github.com/TomGillen/legion">Legion</a>. I especially love the classic ECS interface provided by SPECS, however I also like how Legion makes an ECS look more like a DB, and adds things like cached queries and archetypes, which help a lot with performance. So, I decided to mimic the way of SPECS&#39; API. For example, SPECS can be used like this:</p>
<pre><code class="lang-rust">use specs::prelude::*;

#[derive(Debug)]
struct Velocity(f32);

impl Component for Velocity {
    type Storage = VecStorage&lt;Self&gt;;
}

#[derive(Debug)]
struct Position(f32);

impl Component for Position {
    type Storage = VecStorage&lt;Self&gt;;
}

struct SysA;

impl&lt;'a&gt; System&lt;'a&gt; for SysA {
    // These are the resources required for execution.
    type SystemData = (WriteStorage&lt;'a, Position&gt;, ReadStorage&lt;'a, Velocity&gt;);

    fn run(&amp;mut self, (mut pos, vel): Self::SystemData) {
        // The `.join()` combines multiple component storages,
        // so we get access to all entities which have
        // both a position and a velocity.
        for (pos, vel) in (&amp;mut pos, &amp;vel).join() {
            pos.0 += vel.0;
        }
    }
}

fn main() {
    // The `World` is our container for components and other resources.
    let mut world = World::new();
    world.register::&lt;Position&gt;();
    world.register::&lt;Velocity&gt;();

    world.create_entity().with(Velocity(2.0)).with(Position(0.0)).build();
    world.create_entity().with(Velocity(4.0)).with(Position(1.6)).build();
    world.create_entity().with(Velocity(1.5)).with(Position(5.4)).build();
    world.create_entity().with(Position(2.0)).build();

    // This builds a dispatcher.
    let mut dispatcher = DispatcherBuilder::new().with(SysA, "sys_a", &amp;[]).build();
    dispatcher.setup(&amp;mut world);

    // This dispatches all the systems in parallel (but blocking) once.
    dispatcher.dispatch(&amp;mut world);
}
</code></pre>
<p>Quiet a bit going on there, however it is a simple piece of code which defines two components, <code>Position</code> and <code>Velocity</code> and a system, <code>SysA</code>, which iterates over the entities that have to be created at runtime in <code>main()</code>. In the SPECS API, there is some baggage, which is unnecessary, because it could be done in constructors (see the call to <code>setup()</code>), or automatically recognized by the system (component registration, for one). Also, one major flaw of SPECS is that it has to do the join of components, which creates a set with the needed components, each time a system is executed (each time <code>SysA::run()</code> is executed). That&#39;s a terrible overhead, and it shows in benchmarks. Which is why I decided to take some under-the-hood-inspiration from Legion, which will replace SPECS in Amethyst for performance reasons, among others. The result is a cleaner API, which only exposes the least necessary actions to a library user. Writing the same example as SPECS uses, just for sim-ecs in TypeScript, yields:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {ECS, ISystemActions, Read, System, SystemData, Write} <span class="hljs-keyword">from</span> <span class="hljs-string">"sim-ecs"</span>;

<span class="hljs-keyword">class</span> Velocity {
    speed: <span class="hljs-built_in">number</span> = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params">speed: <span class="hljs-built_in">number</span></span>) { <span class="hljs-keyword">this</span>.speed = speed; }
}

<span class="hljs-keyword">class</span> Position {
    x: <span class="hljs-built_in">number</span> = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">constructor</span>(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) { <span class="hljs-keyword">this</span>.x = x; }
}

<span class="hljs-keyword">class</span> Data <span class="hljs-keyword">extends</span> SystemData {
    pos = Write(Position);
    vel = Read(Velocity);
}

<span class="hljs-keyword">class</span> SysA <span class="hljs-keyword">extends</span> System&lt;Data&gt; {
    readonly SystemDataType = Data;

    <span class="hljs-keyword">async</span> run(actions: ISystemActions, dataSet: Set&lt;Data&gt;): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; {
        <span class="hljs-keyword">let</span> pos, vel;
        <span class="hljs-keyword">for</span> ({pos, vel} of dataSet) {
            pos.x += vel.speed;
        }
    }
}

main: {
    <span class="hljs-keyword">const</span> ecs = <span class="hljs-keyword">new</span> ECS();
    <span class="hljs-keyword">const</span> world = ecs.createWorld();

    world.buildEntity().with(<span class="hljs-keyword">new</span> Velocity(<span class="hljs-number">2</span>)).with(<span class="hljs-keyword">new</span> Position(<span class="hljs-number">0</span>)).build();
    world.buildEntity().with(<span class="hljs-keyword">new</span> Velocity(<span class="hljs-number">4</span>)).with(<span class="hljs-keyword">new</span> Position(<span class="hljs-number">1.6</span>)).build();
    world.buildEntity().with(<span class="hljs-keyword">new</span> Velocity(<span class="hljs-number">1.5</span>)).with(<span class="hljs-keyword">new</span> Position(<span class="hljs-number">5.4</span>)).build();
    world.buildEntity().with(<span class="hljs-keyword">new</span> Position(<span class="hljs-number">2</span>)).build();

    world.addSystem(<span class="hljs-keyword">new</span> SysA());

    world.dispatch();
}
</code></pre>
<p>Alternatively, you can check out the <a target='_blank' rel='noopener noreferrer'  href="https://github.com/NSSTC/sim-ecs/blob/master/examples/counter.ts">counter example</a>, which includes a lot of descriptions and additionally features a continuously running world and resources, which are pretty useful.</p>
<p>Anyway, as you can see above, I had to add a constructor to the Components, because Rust works a bit different, not having constructors, but requiring fields to be filled by name or position on initialization. That&#39;s not a big issue, though, and as a bonus, I got rid of a few annoyances while maintaining the important features, namely leveraging TypeScipt&#39;s type system (you get auto-complete for the types in a system&#39;s <code>run()</code> method) and JavaScript&#39;s Prototypes, which foregoes all the string-matching, which are a possible error-source. For example this means that you get additional build-time checks on component names, because you have to use the actual classes instead of class names (which was one of the downsides of existing libraries). All in all, I am quiet happy with how I can use the library.</p>
<p>By the way, you can test sim-ecs yourself. Either get the package from NPM</p>
<pre><code>$ <span class="hljs-built_in">npm</span> install sim-ecs
</code></pre><p>or pull it directly from GitHub (in your package.json):</p>
<pre><code class="lang-javascript">{
  <span class="hljs-comment">// ...</span>
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-string">"sim-ecs"</span>: <span class="hljs-string">"github:NSSTC/sim-ecs#0.1.0"</span>
  }
}
</code></pre>
<p>If you use the github version, make sure that you use the latest tag or master branch, else you will get stale, outdated code ;)</p>
<h2 id="about-challenges-and-hurdles">About challenges and hurdles</h2>
<p>Implementing the above, however, was not easy. It meant exploring the limits of JS and TS. For a long time, I wasn&#39;t even sure if I could copy the API the way it was, because Rust has a ton of cool and novel features. Creating sim-ecs was no walk in the park, and here are two of the things which took me over the limits of what I knew was possible.</p>
<h3 id="manual-run-time-object-to-type-assignment">Manual run-time object-to-type assignment</h3>
<p>This was the thing which had me floating the most. From the beginning I spent wondering, if it is possible to declare a datatype and have it filled with the correct components, and on top of that get auto-completion working for that. Auto-completion was a big issue, because it is a valuable tool when developing an application, and I did not want to miss it. So, I didn&#39;t implement it for a long time, and instead just passed an array of entities to the <code>run()</code> method, hoping some day I would have a Heureka moment. Spoiler: It did come. However, it only came after I created the rest of the ECS using lots of Prototypes everywhere, so much that I became very comfortable working with them. All I had to do was declare the fields of a class the type of components I wanted to use, and then somehow also pass that information in a JS-useable way without having to initialize them - which happens to be the prototypes of said components. Under the hood, the ECS would match the prototype names of all components against the prototype information of each field of the <code>Data</code> object in order to identify the entities which may go into the data set of a system - and then create a new instance of <code>Data</code>, which however is filled with the entity&#39;s component refs. Does this sound like an awful lot of loops to you?</p>
<h3 id="crud-operations-on-entities-and-components-are-slow-at-run-time">CRUD operations on entities and components are slow at <code>run()</code>-time</h3>
<p>The other big issue I had was that while I stored all entities (and later information chunks) in a field on each system, which meant simple iteration over continuous data, actually changing anything was - and still is - an expensive task. That&#39;s why, in the beginning, I introduced <code>-Quick-()</code> methods, which would change arrays in the <code>world</code> object, but not start re-sorting the systems. I also only had the <code>maintain()</code> method, which would do all the sorting and updating, which means that all the things were sorted, even if they did not need to be updated. At some point, I came up with the idea of restricting the passed <code>world</code> object and scope it to what a developer should do at a certain time, leaving out dangerous operations. For example, when inside a system&#39;s <code>run()</code> method, a developer should not need to add resources, and definitely should not change components or entities, because that would mean resorting, and that would change the data of other systems, which may or may not have run, yet, which would be pretty much the same as undefined behavior. Danger zone! However, implementing these scopes also let me proxy certain calls, like adding entities, so that I could actually control precisely what is updated, in what way and when - which means only doing what is needed on the live-system. Instant performance-boost, and a safer API!</p>
<h2 id="what-s-next-">What&#39;s next?</h2>
<p>For now, I have a few things left for the 1.0 milestone. However, they mostly come down to under-the-hood improvements and paper work. I need better docs, benchmarks, a CI pipeline. In addition, I want to implement change detection on components and improved archetype handling, which might change the API slightly (see that dispatcher builder in the SPECS example? It makes a lot of sense for a lot of reasons). Also, I need feedback from other people, since I want this to be an active project, which can be used by other people. Is the API intuitive? Are there any big errors on my side? I am mostly concerned with the API, since once I release 1.0, I can easily fix things under the hood, but I cannot easily touch the interface anymore.</p>
<p>Once I have confidently published 1.0, I want to start working on optimizations, which push sim-ecs further, making it really fast and leveraging everything JS VMs are capable of. This mostly comes down to multi-threading and better algorithms for scheduling and execution planning, which are the most powerful in a multi-threaded environment.</p>
<p>If you like what you see, I want to invite you to test sim-ecs out, or contribute. There are a lot of tasks for beginners and also harder tasks in the <a target='_blank' rel='noopener noreferrer'  href="https://github.com/NSSTC/sim-ecs/issues">issue tracker</a>. Even if you just want to write a bug or finding, I would be very happy about you speaking up.</p>
<p>Thank you for reading my article, and see you around!</p>
<hr>
<p>Unbezahlte Werbung durch Nennung und Verlinkung von Produkten, Personen, Organisationen oder Unternehmen.</p>
]]></content:encoded></item><item><title><![CDATA[TIL: Trapezoid tabs in pure CSS]]></title><description><![CDATA[Trapezoid elements plus pure CSS dynamic content. If you ever tried to get one of them done, you know how horrible they are to implement. They require a bit of out-of-the-box thinking, and a lot of tinkering.
My little sister, Marci, asked me for hel...]]></description><link>https://blog.marco-alka.de/til-trapezoid-tabs-in-pure-css</link><guid isPermaLink="true">https://blog.marco-alka.de/til-trapezoid-tabs-in-pure-css</guid><category><![CDATA[CSS]]></category><category><![CDATA[CSS3]]></category><category><![CDATA[TIL]]></category><dc:creator><![CDATA[Marco Alka]]></dc:creator><pubDate>Sat, 18 Apr 2020 02:11:08 GMT</pubDate><content:encoded><![CDATA[<p>Trapezoid elements plus pure CSS dynamic content. If you ever tried to get one of them done, you know how horrible they are to implement. They require a bit of out-of-the-box thinking, and a lot of tinkering.</p>
<p>My little sister, <a class="user-mention" href="https://hashnode.com/@MarciAlka">Marci</a>, asked me for help creating a tab navigation for a website, after she wasn&#39;t able to quite nail it after hours of trial and google-foo and trying her best. So I sat down and worked out a solution, which I want to share with the rest of you, too!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" data-card-width="600px" data-card-key="2e4d628b39a64b99917c73956a16b477" href="https://codepen.io/minecrawler/pen/eYpJdqJ" data-card-controls="0" data-card-theme="light">https://codepen.io/minecrawler/pen/eYpJdqJ</a></div>
<p>The idea was that the tabs can be clicked to navigate to another site, with one tab active at a time. The solution should be template-able, and, using a script, it should also be possible to pre-fill the content for better UX (no site changes necessary).</p>
<p>After googling for a bit, I came across <a target='_blank' rel='noopener noreferrer'  href="https://css-tricks.com/the-shapes-of-css/">creating shapes in pure CSS</a>. You can easily do so by setting an element&#39;s height to 0, and instead use the borders to create the shape. For example, if I set the bottom border height to 100px, and then add a width for transparent side-borders, the bottom-border will become a trapezoid.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1587174735340/V-Ym3LvHo.png" alt="2020-04-18_03h51_52.png"></p>
<p>With an element-width of zero, it will even look like a triangle. That&#39;s because of how the borders are drawn by the browser. They actually don&#39;t overlap, but are angled where they meet the other borders.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1587175288955/qBGOYsFfS.png" alt="2020-04-18_04h01_19.png"></p>
<p>The next problem was putting text on the trapezoid. I decided to just go for a second element, which I draw on top of the trapezoid border using a negative margin. Last but not least, I had to add a border to the border-trapezoid. For that, I thought it would be pretty cool to not introduce yet another HTML element, but go for a pseudo-element. So I added a <code>:before</code> selector, which I then filled with another trapezoid bottom border, which is just a little bit wider and higher than the original element, and is z-ordered behind the original element. That one would be black to form the tab border. The result is a bordered, trapezoid tab with text on top.</p>
<p>That covers the aesthetics. What&#39;s left is the dynamic content. There is a standard trick to that one. Just create radio buttons, hide them and make the tab text labels for the radio buttons. In CSS, it is possible to use a <code>:selected</code> selector, so that we can have a kind of conditional drawing. Add either the <code>+</code> or <code>~</code> sibling operator in the mix, and the radio buttons actually can trigger CSS styling on other (sibling) elements - in pure CSS! The only downside is that we need to use sibling elements, which requires careful placement of the content. Also, the content now is in a non-intuitive spot in HTML. It should not be inside the tab-bar, however it&#39;s a compromise for this hack.</p>
<h2 id="wouldn-t-a-script-have-been-easier-">Wouldn&#39;t a script have been easier?</h2>
<p>While it&#39;s true that we could have put some script in place which allows for a lot simpler solutions (including using a canvas element to draw the tabs), JS is not available everywhere. Especially software for accessibility and bots often cannot not execute JS, leaving the website in a broken, unusable state or leads to bots not being able to discover every part of your website. Bad accessibility may even lead to legal consequences, depending on where you live. Plus, in order to get a high lighthouse-score (which means a better Google ranking), we need to make sure that we don&#39;t use JS to render core parts of our website. As such, creating a script-less, working website which can be enhanced with styling and scripts still is important, even today. You can read more about Progressive Enhancement on <a target='_blank' rel='noopener noreferrer'  href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">MDN</a>, <a target='_blank' rel='noopener noreferrer'  href="https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/">Smashing Mag</a>, <a target='_blank' rel='noopener noreferrer'  href="https://www.shopify.com/partners/blog/what-is-progressive-enhancement-and-why-should-you-care">Shopify</a> and other sites.</p>
]]></content:encoded></item></channel></rss>