<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oleg Merkulov</title>
    <description>The latest articles on DEV Community by Oleg Merkulov (@oleg_merkulov).</description>
    <link>https://dev.to/oleg_merkulov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4123542%2Fde18ff8a-8f8e-4cfa-91ba-8e60597993d3.jpg</url>
      <title>DEV Community: Oleg Merkulov</title>
      <link>https://dev.to/oleg_merkulov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oleg_merkulov"/>
    <language>en</language>
    <item>
      <title>Rendering an Angular Application in Minecraft</title>
      <dc:creator>Oleg Merkulov</dc:creator>
      <pubDate>Mon, 14 Sep 2026 20:11:55 +0000</pubDate>
      <link>https://dev.to/oleg_merkulov/rendering-an-angular-application-in-minecraft-4nol</link>
      <guid>https://dev.to/oleg_merkulov/rendering-an-angular-application-in-minecraft-4nol</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is an English translation of my article originally published in Russian on Habr: &lt;a href="https://habr.com/ru/articles/1081682/" rel="noopener noreferrer"&gt;Rendering an Angular Application in Minecraft&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea behind this experiment is to render an Angular application in Minecraft. Building a redstone computer capable of running Node.js could take half a lifetime, so the application runs externally in Node.js, with Minecraft acting as its screen.&lt;/p&gt;

&lt;p&gt;The experiment involves building a weather application that can be controlled from inside the game world. Behind the interface are ordinary Angular building blocks.&lt;/p&gt;

&lt;p&gt;To make this work, we need to connect three things: Angular's operations on elements, layout calculations, and Minecraft commands. Let's start by choosing how to draw the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing in Minecraft with RCON and block_display
&lt;/h2&gt;

&lt;p&gt;RCON is the most convenient option for programmatic drawing. However, it requires running the world on a server with access to its internal settings. We do this locally by downloading the server from the official website. RCON is an interface for executing console commands remotely. Using the npm library &lt;code&gt;rcon-client&lt;/code&gt;, the application sends commands from Node.js to the game world.&lt;/p&gt;

&lt;p&gt;The first option is obvious: treat each block as a pixel. The &lt;code&gt;setblock&lt;/code&gt; command places a single block, while &lt;code&gt;fill&lt;/code&gt; fills a rectangular region. You can build a screen this way, and the project supports this mode too. But even a 200 × 100 pixel image requires 20,000 block positions. Updating the interface means changing regions of the world, and each pixel is limited to the size of a whole block. This is a fairly expensive operation, yet a screen with that resolution is still too small. That is why we will use &lt;code&gt;block_display&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This entity displays a block model and lets you change its size and position. These entities have been available since Minecraft Java Edition 1.19.4. It is a virtual block: visible in the world, but without physical interactions. We can turn a block model into a thin rectangle and place it anywhere on the screen.&lt;/p&gt;

&lt;p&gt;The configuration uses &lt;code&gt;pixelsPerBlock: 4&lt;/code&gt;: four pixels horizontally and four vertically across the area of one block face. That gives us 16 pixels instead of one. An 880 × 550 pixel screen occupies 220 × 137.5 blocks. This resolution is sufficient to display and comfortably use a small application. Now we need to work out the rendering itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular rendering and a custom platform
&lt;/h2&gt;

&lt;p&gt;In a typical browser application, an Angular component contains data and a template describing the interface. Compilation turns that template into Angular instructions. When the view is created, these instructions create elements and text nodes, connect them, set attributes, and register event handlers.&lt;/p&gt;

&lt;p&gt;When component state changes, Angular evaluates bindings during change detection and updates those whose values differ from the previous ones. For example, a new temperature value updates the text in the corresponding node. There is no need to recreate the entire interface.&lt;/p&gt;

&lt;p&gt;Node operations go through a renderer. In the browser implementation, they modify the DOM, after which the browser calculates element positions and draws the result. Angular manages the view structure and bindings; the browser handles geometry and pixels. To output to Minecraft, we need to replace that last part: store our own nodes instead of DOM nodes, calculate their layout with Yoga, and display the result as entities in the game world.&lt;/p&gt;

&lt;p&gt;Angular lets us create custom platforms and renderer implementations. A platform assembles the dependencies for a particular environment, while the renderer determines what happens when an element is created, text changes, or a style is set.&lt;/p&gt;

&lt;p&gt;Let's call the platform &lt;code&gt;platformMinecraft&lt;/code&gt;. It is created with &lt;code&gt;createPlatformFactory&lt;/code&gt;, using &lt;code&gt;platformCore&lt;/code&gt; as its base. At startup, it receives the RCON connection, screen parameters, and font settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;platformMinecraft&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MC_RCON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rcon&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MC_SCREEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MC_FONT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;bootstrapModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, it registers &lt;code&gt;MinecraftRendererFactory&lt;/code&gt;, which returns the Minecraft renderer. This implements &lt;code&gt;Renderer2&lt;/code&gt;: &lt;code&gt;createElement&lt;/code&gt;, &lt;code&gt;createText&lt;/code&gt;, &lt;code&gt;appendChild&lt;/code&gt;, &lt;code&gt;setStyle&lt;/code&gt;, &lt;code&gt;setValue&lt;/code&gt;, and the other methods Angular uses to work with interface nodes.&lt;/p&gt;

&lt;p&gt;Here is a simple template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background: black_concrete; color: white_wool"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{ temperature() }}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the view is created, the renderer receives operations to create the element and text, connect them, and set attributes. When &lt;code&gt;temperature()&lt;/code&gt; changes, Angular updates the text through &lt;code&gt;setValue&lt;/code&gt;. The Minecraft renderer turns these operations into an image.&lt;/p&gt;

&lt;p&gt;To do this, it maintains a separate tree of &lt;code&gt;McNode&lt;/code&gt; objects. Each node has a parent, children, attributes, and styles. Text nodes contain a string; image nodes contain a decoded image. Angular calls renderer methods, and those methods modify this tree. For example, adding a child node looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChildCount&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markDirty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child is added to two trees at once: the &lt;code&gt;McNode&lt;/code&gt; element tree and the Yoga tree, which handles sizes and positions. Finally, the scene is marked as needing an update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating Yoga and calculating layout
&lt;/h2&gt;

&lt;p&gt;Creating a tree is not enough. We need to know where a card starts, how much space its heading takes, and where to put a button after the text. In a browser, the layout engine handles this. On the Minecraft platform, Yoga will do that work.&lt;/p&gt;

&lt;p&gt;Yoga calculates rectangle sizes and coordinates using rules based primarily on Flexbox. The engine itself draws nothing. It receives an element structure and supported styles, and returns geometry. For each node, we can get its position relative to its parent and its size, then convert them into screen coordinates &lt;code&gt;{ x, y, w, h }&lt;/code&gt; for drawing.&lt;/p&gt;

&lt;p&gt;For example, a container 400 pixels wide and 120 pixels high arranges two elements in a row. It has 16 pixels of padding on all sides and a 12 pixel gap between elements. The first element has a width of 120 and a height of 80 pixels. The second has &lt;code&gt;flex: 1&lt;/code&gt; and stretches vertically because its parent uses &lt;code&gt;align-items: stretch&lt;/code&gt;. After &lt;code&gt;calculateLayout()&lt;/code&gt;, calling &lt;code&gt;getComputedLayout()&lt;/code&gt; on each child gives the following values. These are the four result fields we need for drawing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// First element&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Second element&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;148&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second element starts after the left padding, the first element, and the gap: 16 + 120 + 12 = 148. Its width fills the remaining space: 400 − 16 − 16 − 120 − 12 = 236. Its height is the available height between the padding: 120 − 16 − 16 = 88.&lt;/p&gt;

&lt;p&gt;If the container itself is at &lt;code&gt;{ x: 32, y: 24 }&lt;/code&gt; on the screen, the renderer adds this offset and gets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Rectangles in screen coordinates&lt;/span&gt;
&lt;span class="c1"&gt;// [{ x: 48, y: 40, w: 120, h: 80 }, { x: 180, y: 40, w: 236, h: 88 }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not Minecraft block coordinates yet. Yoga calculates layout in interface units, which we treat as pixels here. The renderer converts them into world coordinates using the screen's scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting renderer elements to Yoga nodes
&lt;/h3&gt;

&lt;p&gt;Yoga is integrated directly into the custom renderer. Creating an &lt;code&gt;McNode&lt;/code&gt; also creates its corresponding Yoga node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Yoga&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yoga-layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McNode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;McNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Yoga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The renderer's &lt;code&gt;createElement&lt;/code&gt; and &lt;code&gt;createText&lt;/code&gt; methods use this function. When adding a child, the &lt;code&gt;appendChild&lt;/code&gt; method shown above calls &lt;code&gt;parent.yogaNode.insertChild(...)&lt;/code&gt;. Removal uses the Yoga node's &lt;code&gt;removeChild&lt;/code&gt;. This keeps the layout structure synchronized with the tree managed by Angular.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting styles into Yoga parameters
&lt;/h3&gt;

&lt;p&gt;For example, a template can define this layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"flex-direction: row; padding: 16px; gap: 12px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width: 120px; height: 80px; background: blue_concrete"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"flex: 1; background: gray_concrete"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first element gets a width of 120 pixels. The second takes the remaining space. Yoga accounts for the padding and the gap between them. The renderer translates &lt;code&gt;style&lt;/code&gt; properties into calls such as &lt;code&gt;setWidth&lt;/code&gt;, &lt;code&gt;setPadding&lt;/code&gt;, and &lt;code&gt;setFlex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For dynamic styles, Angular calls &lt;code&gt;setStyle&lt;/code&gt;. In the renderer, this method passes the value to &lt;code&gt;applyStyle&lt;/code&gt; and marks the scene as needing an update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;RendererStyleFlags2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;applyStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markDirty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;applyStyle&lt;/code&gt;, property names are normalized to camelCase, and size values are parsed by &lt;code&gt;toLen&lt;/code&gt;. Here is part of the property handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;toLen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;toLen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFlex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;toLen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setGap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Gutter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;All&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;toLen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A static &lt;code&gt;style="..."&lt;/code&gt; attribute arrives through &lt;code&gt;setAttribute&lt;/code&gt;. Its contents are split into individual declarations, each of which is also passed to &lt;code&gt;applyStyle&lt;/code&gt;. A static width and a &lt;code&gt;[style.width]&lt;/code&gt; binding therefore end up changing the same Yoga node. Colors are handled separately: &lt;code&gt;background&lt;/code&gt; and &lt;code&gt;color&lt;/code&gt; are stored in &lt;code&gt;McNode&lt;/code&gt; for painting and do not participate in layout calculation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recalculating layout when the view changes
&lt;/h3&gt;

&lt;p&gt;The renderer factory sets the root Yoga node's width and height to the screen dimensions. The &lt;code&gt;markDirty&lt;/code&gt; callback passed to &lt;code&gt;MinecraftRenderer&lt;/code&gt; sets the factory's &lt;code&gt;dirty&lt;/code&gt; flag, indicating that the scene needs updating.&lt;/p&gt;

&lt;p&gt;After a view update pass, Angular calls &lt;code&gt;RendererFactory2.end()&lt;/code&gt;. The Minecraft implementation schedules recalculation here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dirty&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scheduled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scheduled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scheduled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combines several element changes into one scheduled &lt;code&gt;flush()&lt;/code&gt; call. It starts by running Yoga's layout calculation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LTR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;collectPaint&lt;/code&gt; traverses the tree and reads each visible node's computed layout. Yoga coordinates are relative to the parent, so accumulated offsets are added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedLayout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These coordinates are used to assemble background rectangles and text and image pixels. The resulting collection is passed to &lt;code&gt;MinecraftPainter&lt;/code&gt;, which builds the Minecraft commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1k0srzn8whiy26jhuurf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1k0srzn8whiy26jhuurf.png" alt="Layout rendered in Minecraft" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: Rendering the layout.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring text and images
&lt;/h2&gt;

&lt;p&gt;Text and images present an additional problem. Yoga does not know how much space a line occupies in the selected font, or the original dimensions of a PNG. For these elements, we provide a &lt;a href="https://www.yogalayout.dev/docs/advanced/external-layout-systems" rel="noopener noreferrer"&gt;measure function&lt;/a&gt;. The engine passes constraints to it, and it returns the content's dimensions.&lt;/p&gt;

&lt;p&gt;For an image with only its width specified, the height can be derived from the original aspect ratio. For text, we need to measure characters and calculate line wrapping. If a label gets longer, its height may change and move the elements below it. That is why changing text also marks its Yoga node with &lt;code&gt;markDirty()&lt;/code&gt;, so its dimensions are recalculated.&lt;/p&gt;

&lt;p&gt;There are two separate change indicators here: the factory's &lt;code&gt;dirty&lt;/code&gt; flag schedules a screen update, while &lt;code&gt;yogaNode.markDirty()&lt;/code&gt; tells Yoga that the content needs measuring again. When text changes, the renderer sets both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yogaNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markDirty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markDirty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After layout, we know the sizes and coordinates of the element rectangles. Filling their backgrounds is straightforward. Now we need to fill them with content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rasterizing text
&lt;/h2&gt;

&lt;p&gt;We could draw text on a canvas, read its pixels, and transfer them to Minecraft. The current implementation uses &lt;code&gt;opentype.js&lt;/code&gt; without a canvas.&lt;/p&gt;

&lt;p&gt;The library reads TTF and OTF files and provides access to glyph outlines—the graphical shapes of characters. The rasterizer approximates the outline's curves with line segments and checks which pixels fall inside each letter.&lt;/p&gt;

&lt;p&gt;A single check at the center of a pixel is not enough for thin strokes: the result depends too much on how the outline aligns with the grid. Instead, we test nine points inside each pixel, arranged in a 3 × 3 grid. If the proportion of points inside the outline reaches a threshold, the pixel is filled.&lt;/p&gt;

&lt;p&gt;This is supersampling. Here, it helps determine the shape of a letter on a coarse grid. Each resulting pixel is either filled or omitted; there is no semitransparent text antialiasing.&lt;/p&gt;

&lt;p&gt;Character dimensions are also used during layout. Lines wrap at word boundaries, and words that are too long are split into individual characters. Outlines and rasterized glyphs are cached so that the same letter at the same size does not need to be calculated again.&lt;/p&gt;

&lt;p&gt;The output is a list of pixel coordinates. Their color comes from the &lt;code&gt;color&lt;/code&gt; property, while &lt;code&gt;font-family&lt;/code&gt; selects the font file from the families registered in the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd20ltqb4ewsucrvckzn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd20ltqb4ewsucrvckzn6.png" alt="Text rendered in Minecraft" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: Rendering text.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Processing images and matching colors
&lt;/h2&gt;

&lt;p&gt;Images are simpler: the pixels are already in the file. PNG and JPEG files are decoded using &lt;code&gt;pngjs&lt;/code&gt; and &lt;code&gt;jpeg-js&lt;/code&gt;, then resized to the dimensions calculated by Yoga. Downscaling averages the colors of the source region while accounting for transparency; upscaling selects a source pixel. Pixels whose alpha is below the threshold are skipped. The finished result is cached.&lt;/p&gt;

&lt;p&gt;Next, we need to choose a block for each color. The palette includes concrete, wool, and terracotta. Each material has an approximate average texture color. The renderer compares it with the desired RGB value using the weighted redmean distance and selects the closest match. This handles both image pixels and style colors such as &lt;code&gt;#a144ff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want a specific material, you can write &lt;code&gt;background: black_concrete&lt;/code&gt; or &lt;code&gt;color: white_wool&lt;/code&gt; directly. Exact matches to browser colors are not guaranteed: the material palette is limited, and blocks retain their textures. Still, even the Angular logo ends up assembled from Minecraft materials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fschxgzxf1i8oibqr4qa5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fschxgzxf1i8oibqr4qa5.png" alt="Images rendered in Minecraft" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: Rendering images.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing drawing and entity updates
&lt;/h2&gt;

&lt;p&gt;At this point, the renderer knows where the backgrounds and the text and image pixels belong. Sending a separate command for every pixel would make the screen too resource intensive.&lt;/p&gt;

&lt;p&gt;A container's background is therefore drawn as one rectangle from the start. Text and image pixels are first merged into horizontal runs of the same material. If runs on adjacent rows have the same position, width, and depth, they are merged vertically. For example, a solid vertical stroke in a letter can become a single elongated entity.&lt;/p&gt;

&lt;p&gt;Such a rectangle requires just one &lt;code&gt;summon minecraft:block_display&lt;/code&gt; command with the appropriate scale, material, and offset. To keep text visible above the background, layers are separated slightly in depth. The deeper an element is nested in the tree, the closer its image is to the viewer.&lt;/p&gt;

&lt;p&gt;The next optimization concerns updates. The renderer stores the previous collection of rectangles and compares it with the new one. Matching fragments stay in place, new ones are created with &lt;code&gt;summon&lt;/code&gt;, and fragments that disappeared are removed with &lt;code&gt;kill&lt;/code&gt;. The comparison includes coordinates, dimensions, material, and depth.&lt;/p&gt;

&lt;p&gt;If the temperature changes, we do not need to recreate the background or the other unchanged parts of the interface. If the whole image remains unchanged, no drawing commands are sent. Recalculation is triggered after Angular changes through &lt;code&gt;RendererFactory2.end()&lt;/code&gt;: first Yoga updates the geometry, then the renderer assembles the rectangles and sends the difference to Minecraft.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling player actions and UI events
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Renderer2&lt;/code&gt; provides &lt;code&gt;listen()&lt;/code&gt; for events. Angular uses it to register handlers that run in response to the corresponding player action. Events are triggered by approaching an element. Every 500 ms, the application queries player positions through RCON.&lt;/p&gt;

&lt;p&gt;The player's position, adjusted for eye height, is projected onto the screen plane. The coordinates calculated by Yoga are then used to identify the element under that point. If the player is close enough and enters a new element's region, &lt;code&gt;mouseenter&lt;/code&gt; and &lt;code&gt;click&lt;/code&gt; are triggered. Leaving the region triggers &lt;code&gt;mouseleave&lt;/code&gt;. To press the same button again, the player must leave its region and return.&lt;/p&gt;

&lt;p&gt;Neither the direction the player is looking nor mouse button presses are taken into account. The screen configuration allows a distance of up to 40 blocks from its plane. This means the interface is controlled by player movement, so the buttons are made large.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;click&lt;/code&gt; event supports bubbling through the tree and &lt;code&gt;stopPropagation()&lt;/code&gt;. The handler runs inside the Angular zone so that state changes trigger an application update.&lt;/p&gt;

&lt;p&gt;From the component's perspective, everything is familiar: &lt;code&gt;(mouseenter)&lt;/code&gt; enables highlighting, &lt;code&gt;(mouseleave)&lt;/code&gt; removes it, and &lt;code&gt;(click)&lt;/code&gt; invokes a handler. The button notifies its parent that it has been pressed, the parent changes the selected day, and Angular updates the text and styles. The next forecast appears in Minecraft.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing misaligned text fragments
&lt;/h2&gt;

&lt;p&gt;Testing the renderer in the game client revealed a problem: text looked aligned in the local PNG preview, but individual fragments of small letters were displaced in Minecraft. The strokes did not join up.&lt;/p&gt;

&lt;p&gt;Initially, each entity was created at its own center using fractional world coordinates. The model itself was shifted back by half its size. The calculations placed the boundaries of adjacent rectangles together, but the game client showed discrepancies.&lt;/p&gt;

&lt;p&gt;Changing the coordinate system solved the problem. All screen entities are now created at a single integer-coordinate point near the center of the screen. Each rectangle's position is specified as a local translation relative to that common point.&lt;/p&gt;

&lt;p&gt;The screen is far from the world's origin, around &lt;code&gt;X = -24535&lt;/code&gt;. Coordinate precision was considered during the investigation. After switching to a shared origin, the misalignment disappeared, as verified in the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Renderer capabilities and limitations
&lt;/h2&gt;

&lt;p&gt;The resulting renderer supports Flexbox through Yoga, dimensions and spacing, rectangular fills, text, PNG/JPEG images, and player proximity events. This is enough for the weather application.&lt;/p&gt;

&lt;p&gt;CSS classes are not processed, so styles are defined inline and through Angular bindings. There are no shadows, rounded corners, or complete browser layout rules. Components that access the DOM directly need adaptation.&lt;/p&gt;

&lt;p&gt;Update speed depends on the number of changed rectangles, communication over RCON, and the client's entity rendering. Without comparative performance measurements, we cannot claim that this approach is always faster than drawing with real blocks.&lt;/p&gt;

&lt;p&gt;The renderer lets an Angular component load weather data, respond to player actions, and update an interface in the game world. To switch to tomorrow's forecast, you walk up to the “Next” button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically rebuilding and restarting the application
&lt;/h2&gt;

&lt;p&gt;For development, we add an automatic update mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;dev.mjs&lt;/code&gt; script watches files in &lt;code&gt;src&lt;/code&gt;. When a template or TypeScript file is saved, it starts a build and, if that succeeds, restarts the application. On startup, old screen entities are removed and the interface is drawn again. There is no need to run the build manually or enter commands in Minecraft.&lt;/p&gt;

&lt;p&gt;For example, you can change &lt;code&gt;font-size: 28px&lt;/code&gt; to &lt;code&gt;font-size: 36px&lt;/code&gt; in the weather metric component and save the file. After the build and restart, Yoga calculates the new dimensions, and the text on the in-game screen gets larger. You can also change spacing, colors, and element positions, then evaluate the result in Minecraft as soon as the update completes.&lt;/p&gt;

&lt;p&gt;The workflow resembles hot reload, but technically it uses a rebuild and a full restart of the Node.js process. Application state is not preserved: the selected day resets and the forecast is loaded again. The update takes as long as building, starting, and drawing require. If the build fails, the previous version of the application keeps running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture overview
&lt;/h2&gt;

&lt;p&gt;The application runs in Node.js on &lt;code&gt;platformMinecraft&lt;/code&gt;. Angular creates the view and updates bindings through a custom &lt;code&gt;Renderer2&lt;/code&gt;. The renderer maintains the &lt;code&gt;McNode&lt;/code&gt; tree, passes its structure and styles to Yoga, and recalculates sizes and coordinates after changes.&lt;/p&gt;

&lt;p&gt;This geometry is used to assemble the image: backgrounds become rectangles, text is rasterized from font outlines, and PNG and JPEG images are scaled to the required sizes. Colors are replaced with Minecraft materials. Pixels of the same color are merged, the new collection of rectangles is compared with the previous one, and RCON commands are sent only for fragments that changed. In the world, the result is displayed using &lt;code&gt;block_display&lt;/code&gt; entities with fractional scaling and a shared origin.&lt;/p&gt;

&lt;p&gt;The return path starts by polling player coordinates. &lt;code&gt;MinecraftInput&lt;/code&gt; projects them onto the screen, checks whether they fall within element boundaries, and invokes handlers registered through &lt;code&gt;listen()&lt;/code&gt;. Those handlers change Angular state, starting the rendering cycle again. The interface therefore responds both to new weather data and to player actions.&lt;/p&gt;

&lt;p&gt;UI support is limited to the selected set of styles and events. Yoga handles layout, while the renderer provides the drawing and input that a browser would supply in a normal web application.&lt;/p&gt;

&lt;h2&gt;
  
  
  The weather forecast application
&lt;/h2&gt;

&lt;p&gt;Built on this platform, the application shows a seven-day weather forecast for Dubna. Its 880 × 550 pixel screen contains the date, a weather icon, the selected day's maximum and minimum temperatures, maximum wind speed, and probability of precipitation. Two buttons at the bottom switch between days.&lt;/p&gt;

&lt;p&gt;For example, the application initially shows today's forecast. The player approaches the forward button's region, the button is highlighted, and the application switches to tomorrow. The date, metrics, and weather icon change. The player can continue through the forecast or go back. On the first and last days, the corresponding button is disabled and changes color.&lt;/p&gt;

&lt;p&gt;This is the feedback loop: the player's position in Minecraft becomes an Angular event. The handler changes application state, and the renderer returns the result to the game world. A click is triggered when the player enters the button's region; pressing the mouse button is unnecessary. The mechanism behind these events is described in the section on handling player actions.&lt;/p&gt;

&lt;p&gt;Data comes from Open-Meteo at startup and refreshes every 15 minutes. A loading state is shown during the initial request. If the connection fails, the application keeps the last forecast on screen, shows an error indicator, and retries after one minute.&lt;/p&gt;

&lt;p&gt;In the code, the interface is divided into components: a heading, a weather summary, individual metrics, and navigation. A service handles the data, and the buttons send events to their parent. This application exercises text and image rendering, binding updates, and player interaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Angular component examples
&lt;/h3&gt;

&lt;p&gt;For example, an individual weather metric is an ordinary component with two input properties. It contains neither Minecraft commands nor Yoga calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;weather-metric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex: 1; gap: 6px; justify-content: center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div style="font-size: 16px; color: white_concrete"&amp;gt;{{ label }}&amp;lt;/div&amp;gt;
    &amp;lt;div style="font-size: 28px; color: white_wool"&amp;gt;{{ value }}&amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherMetricComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parent supplies the label and value, and the component defines their appearance. The platform's distinctive feature is visible in the styles: supported inline properties are used instead of CSS classes, and a color can be specified as a block name.&lt;/p&gt;

&lt;p&gt;The root template connects the summary and navigation as follows. This page fragment omits the heading and the loading and error states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (day(); as forecast) {
  &lt;span class="nt"&gt;&amp;lt;weather-summary&lt;/span&gt; &lt;span class="na"&gt;[day]=&lt;/span&gt;&lt;span class="s"&gt;"forecast"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/weather-summary&amp;gt;&lt;/span&gt;
}
&lt;span class="nt"&gt;&amp;lt;weather-day-navigation&lt;/span&gt;
  &lt;span class="na"&gt;[index]=&lt;/span&gt;&lt;span class="s"&gt;"index()"&lt;/span&gt;
  &lt;span class="na"&gt;[count]=&lt;/span&gt;&lt;span class="s"&gt;"weather.days().length"&lt;/span&gt;
  &lt;span class="na"&gt;(step)=&lt;/span&gt;&lt;span class="s"&gt;"move($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/weather-day-navigation&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigation receives the selected day's index and the forecast length. Its buttons emit &lt;code&gt;step.emit(-1)&lt;/code&gt; or &lt;code&gt;step.emit(1)&lt;/code&gt;. The parent component handles the event with this method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;selectedDate&lt;/code&gt; is stored in a signal, while &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;day&lt;/code&gt; are derived using &lt;code&gt;computed&lt;/code&gt;. Changing the selected date updates the data passed to the summary and the binding values in its template.&lt;/p&gt;

&lt;p&gt;The button itself is also an Angular component. Below is a shortened version of &lt;code&gt;McButtonComponent&lt;/code&gt;: it uses fixed color values, while event handling remains unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MinecraftUiEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../minecraft/minecraft-platform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mc-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;padding: 10px 18px; font-size: 18px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[style.background]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disabled ? "gray_concrete" : hovered() ? "cyan_concrete" : "black_concrete"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[style.color]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disabled ? "light_gray_wool" : "white_wool"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(click)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onClick($any($event))&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(mouseenter)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hovered.set(true)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(mouseleave)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hovered.set(false)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;McButtonComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;pressed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MinecraftUiEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;hovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MinecraftUiEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopPropagation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pressed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the player enters the element's region, &lt;code&gt;hovered&lt;/code&gt; changes, updating the background color through its binding. The &lt;code&gt;click&lt;/code&gt; event invokes &lt;code&gt;onClick&lt;/code&gt;, and &lt;code&gt;pressed&lt;/code&gt; notifies the parent of the press. A disabled button does not emit &lt;code&gt;pressed&lt;/code&gt;. All player position detection stays inside &lt;code&gt;MinecraftInput&lt;/code&gt;, so the component works with ready-made events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A custom platform and &lt;code&gt;Renderer2&lt;/code&gt; implementation let us display an Angular application in Minecraft while retaining components, bindings, and event handlers. The application runs in Node.js, Yoga calculates element layout, and the renderer turns the interface into commands for &lt;code&gt;block_display&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The weather forecast demonstrates a complete interaction cycle: service data appears on the in-game screen, player movement triggers button events, and changes to Angular state update the image. Automatic rebuilding lets us test template and component changes directly in Minecraft.&lt;/p&gt;

&lt;p&gt;This kind of output requires implementing geometry calculations, rasterization, and input handling ourselves. Compatibility is therefore limited to the renderer's supported styles and capabilities. Meanwhile, the weather application's logic remains at the level of Angular components, which do not need to send Minecraft commands or calculate world coordinates.&lt;/p&gt;

&lt;p&gt;I'm really glad it worked! The application knows nothing about Minecraft, yet it runs successfully!&lt;/p&gt;

&lt;p&gt;You can see the result and watch the forecast being switched inside the game world &lt;a href="https://youtu.be/Ft3ESg_3pL8" rel="noopener noreferrer"&gt;in this YouTube video&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>minecraft</category>
      <category>node</category>
    </item>
    <item>
      <title>A Turing Machine in Minecraft</title>
      <dc:creator>Oleg Merkulov</dc:creator>
      <pubDate>Sun, 13 Sep 2026 19:51:01 +0000</pubDate>
      <link>https://dev.to/oleg_merkulov/a-turing-machine-in-minecraft-2120</link>
      <guid>https://dev.to/oleg_merkulov/a-turing-machine-in-minecraft-2120</guid>
      <description>&lt;p&gt;&lt;em&gt;This is an English translation of &lt;a href="https://habr.com/ru/articles/1016582/" rel="noopener noreferrer"&gt;my original article on Habr&lt;/a&gt;, originally published in Russian.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A Turing machine is one of the fundamental models in computer science. I've been playing Minecraft for more than 12 years and have built all kinds of mechanisms in the game. After years of university study and working in the industry, I had an idea: combine my hobby with fundamental theory, build a Turing machine using Minecraft's mechanics, and share the result.&lt;/p&gt;

&lt;p&gt;A Turing machine has three basic components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tape:&lt;/strong&gt; a sequence of consecutive cells—infinite in the theoretical model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Head:&lt;/strong&gt; a read/write device positioned over the current tape cell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transition table:&lt;/strong&gt; the machine's program, expressed as a formal set of instructions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Detailed explanations of Turing machine theory are easy to find, so I'll focus on the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redstone: the basis of the simulation
&lt;/h2&gt;

&lt;p&gt;Minecraft has a built-in system for simulating electrical signals: redstone. Let's go over its basic building blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signals and power levels
&lt;/h3&gt;

&lt;p&gt;A redstone signal has 16 power levels, from 0 (no signal) to 15 (maximum strength). Redstone dust carries a signal for up to 15 blocks, losing one power level per block.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring.&lt;/strong&gt; Redstone dust is the main signal conductor. It automatically connects to neighboring dust, blocks, and components to form circuits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key components
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redstone torch:&lt;/strong&gt; an inverting source. It is on by default and turns off when the block it is attached to receives power. It is effectively a NOT gate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lever:&lt;/strong&gt; when switched on, it fully powers the block it is attached to, providing a steady signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Button:&lt;/strong&gt; similar to a lever, but produces a pulse of fixed duration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeater:&lt;/strong&gt; restores a signal to full strength, passes it in only one direction, and introduces a delay of 1–4 redstone ticks. It is essential for long circuits and controlling signal direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comparator:&lt;/strong&gt; compares two signals. In comparison mode, it passes the main input only when it is greater than or equal to the side input. In subtraction mode, it subtracts the side input from the main input. Comparators are useful for more complex logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Logic gates
&lt;/h3&gt;

&lt;p&gt;Torches, repeaters, and dust can be combined into all the basic logic gates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NOT:&lt;/strong&gt; a torch at the end of a wire inverts the signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AND:&lt;/strong&gt; two repeaters feed torches whose outputs converge on a single block with an output torch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OR:&lt;/strong&gt; two wires merge into one; the output is active if at least one input is active.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAND / NOR:&lt;/strong&gt; combinations of the gates above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These primitives form the larger parts of the machine: tape memory, head logic, and the state table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alphabet
&lt;/h3&gt;

&lt;p&gt;For convenience, I chose an alphabet of four symbols. This lets me represent 0 and 1, a special blank symbol indicating an empty cell, and one spare value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory cell
&lt;/h3&gt;

&lt;p&gt;The memory cell stores data on the tape. It contains four bits and can hold values from 0 to 15. Once activated (Figure 1), it continuously outputs the stored signal—in this example, &lt;strong&gt;0101&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The input section on the left has a reset button beneath the first sign. Four bits are more than this implementation needs; I chose that capacity with future extensions in mind.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4jp6dhszl7oct088c1lg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4jp6dhszl7oct088c1lg.png" alt="Memory cell" width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1. Memory cell&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Shift register
&lt;/h3&gt;

&lt;p&gt;To select one of several memory cells, the machine needs a shift register. It has a row of lamps: the lit lamp indicates the cell the head points to.&lt;/p&gt;

&lt;p&gt;On the right, below the signs in Figure 2, there are three buttons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move the head one cell to the left.&lt;/li&gt;
&lt;li&gt;Move the head one cell to the right.&lt;/li&gt;
&lt;li&gt;Reset the shift register to its initial position.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the initial position, the leftmost lamp is on.&lt;/p&gt;

&lt;p&gt;An optional lamp stands separately on the left. It lights up whenever the shift register finishes a move. This completion signal can be used to minimize delays in the main execution loop while ensuring that each shift has finished. In effect, this mechanism implements a bit-shift operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8of7ib12t0g0itnagd56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8of7ib12t0g0itnagd56.png" alt="Shift register" width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2. Shift register&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Program cell
&lt;/h3&gt;

&lt;p&gt;A program cell is the basic storage unit of the transition table. It holds ten bits of information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The first four bits&lt;/strong&gt; specify the value to write to the tape cell currently selected by the head.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The next two bits&lt;/strong&gt; specify the head movement: &lt;strong&gt;10&lt;/strong&gt; means left, and &lt;strong&gt;01&lt;/strong&gt; means right. The cell shown in Figure 3 is configured to move left.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The final four bits&lt;/strong&gt; specify the next state, entered after the head moves. Again, four bits are more than needed here, but leave room for future extensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a program cell is executed, it first writes a value to the tape, then moves the head, and finally switches to the selected state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7bfxva45gv419ymjovu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7bfxva45gv419ymjovu.png" alt="Program cell" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3. Program cell&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Binary-to-decimal decoder
&lt;/h3&gt;

&lt;p&gt;The program is encoded in binary, so the machine needs a way to select physical components using binary values. This decoder is optional, but greatly simplifies both the logic and the construction of the Turing machine.&lt;/p&gt;

&lt;p&gt;In Figure 4, a binary number is supplied on the right—in this case, &lt;strong&gt;10&lt;/strong&gt;. The output on the left indicates the corresponding decimal value: 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2r1zfo4wevvm8qi8nn0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2r1zfo4wevvm8qi8nn0e.png" alt="Binary-to-decimal decoder" width="800" height="425"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4. Binary-to-decimal decoder&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution cycle
&lt;/h3&gt;

&lt;p&gt;The machine starts with its execution loop. The loop uses fixed delays between operations on its components. For example: read a cell, wait two seconds; write to a cell, wait two seconds; and so on.&lt;/p&gt;

&lt;p&gt;The components are not wired into a completion-signaling system. If the loop sends a command to move the head right, it does not receive a signal telling it exactly when that operation finishes. There is no particular technical obstacle to adding this; I used fixed delays to get a working result sooner.&lt;/p&gt;

&lt;p&gt;The overall sequence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the machine.&lt;/li&gt;
&lt;li&gt;Read the symbol in the current memory cell.&lt;/li&gt;
&lt;li&gt;Look up the transition using the current state and the symbol read from the tape.&lt;/li&gt;
&lt;li&gt;Write the value from the transition table to the current cell.&lt;/li&gt;
&lt;li&gt;Move the head.&lt;/li&gt;
&lt;li&gt;Switch to the next state.&lt;/li&gt;
&lt;li&gt;If the state is empty, send a signal to stop the execution loop. Otherwise, return to step 2.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After assembling all the components and connecting the necessary lines, we have a Turing machine in Minecraft.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8bqfpwfyhjjeqgc00wrt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8bqfpwfyhjjeqgc00wrt.png" alt="The Turing machine in Minecraft" width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5. The Turing machine in Minecraft&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Viewed from above, the mechanism's individual components are easier to examine in action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxm5xvkr7r8g7g9v98t2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxm5xvkr7r8g7g9v98t2o.png" alt="Top view of the Turing machine" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6. Top view of the Turing machine&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The numbered components are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The encoded program:&lt;/strong&gt; four states, including one halting state, and four alphabet symbols, including the blank symbol, ε. The build contains 20 basic program cells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The tape:&lt;/strong&gt; eight memory cells, for a total of 32 bits of tape memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The shift register.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The execution loop.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The binary-to-decimal decoders.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The orange wire running from component 1 to component 4 carries the halt signal. Entering the halting state triggers this signal and stops the program's execution loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsfeiklz7vahq5h4uah9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsfeiklz7vahq5h4uah9x.png" alt="Main components, viewed from above" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 7. Main components, viewed from above&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional views
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F89dqc333h3tfgn2t6w8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F89dqc333h3tfgn2t6w8d.png" alt="Close-up view" width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 8. Close-up view&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsney4stxb0768xcu0uaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsney4stxb0768xcu0uaw.png" alt="View of the execution loop" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 9. View of the execution loop&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The result is an interesting, fully functional mechanism implementing a particular instance of a Turing machine. You can &lt;a href="https://www.youtube.com/watch?v=9X8HNgfThac" rel="noopener noreferrer"&gt;watch it in action on YouTube&lt;/a&gt;. The example program increments a number entered on the tape beforehand.&lt;/p&gt;

&lt;p&gt;Play Minecraft and have fun!&lt;/p&gt;




&lt;p&gt;Original article: &lt;a href="https://habr.com/ru/articles/1016582/" rel="noopener noreferrer"&gt;A Turing Machine in Minecraft — Habr (in Russian)&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>minecraft</category>
      <category>computerscience</category>
      <category>angular</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
