<?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>Forem: JoeStrout</title>
    <description>The latest articles on Forem by JoeStrout (@joestrout).</description>
    <link>https://forem.com/joestrout</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F955389%2Ffdd96374-bc61-43f2-bf73-c2d6d6968a72.png</url>
      <title>Forem: JoeStrout</title>
      <link>https://forem.com/joestrout</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joestrout"/>
    <language>en</language>
    <item>
      <title>MiniClaw: A Tiny LLM Agent for Mini Micro</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Thu, 16 Apr 2026 18:13:14 +0000</pubDate>
      <link>https://forem.com/joestrout/miniclaw-a-tiny-llm-agent-for-mini-micro-4akf</link>
      <guid>https://forem.com/joestrout/miniclaw-a-tiny-llm-agent-for-mini-micro-4akf</guid>
      <description>&lt;p&gt;Agents are all the rage these days.  &lt;a href="https://code.claude.com/docs/en/overview" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; was one of the first, and perhaps still the most heavily used, specialized for coding.  Then OpenClaw burst onto the scene, able to do all sorts of general computer-use things, and caused a &lt;a href="https://www.tomshardware.com/tech-industry/artificial-intelligence/openclaw-fueled-ordering-frenzy-creates-apple-mac-shortage-delivery-for-high-unified-memory-units-now-ranges-from-6-days-to-6-weeks" rel="noopener noreferrer"&gt;shortage of Mac Minis&lt;/a&gt;.  More recently, &lt;a href="https://github.com/nousresearch/hermes-agent" rel="noopener noreferrer"&gt;Hermes Agent&lt;/a&gt; is a common favorite, with over 93 thousand stars on GitHub.&lt;/p&gt;

&lt;p&gt;All of these agents work in fundamentally the same way.  A "harness" acts as both the main program for an LLM, controlling its context so that it always knows what it needs to know; and provides tools the LLM can use so that it can always do what it needs to do.&lt;/p&gt;

&lt;p&gt;I covered &lt;a href="https://dev.to/joestrout/use-gpt-3-in-mini-micro-1h63"&gt;accessing LLMs from Mini Micro&lt;/a&gt; back in 2022, and &lt;a href="https://dev.to/joestrout/combining-gpt-and-wolfram-alpha-ma2"&gt;again in 2023&lt;/a&gt;, so why don't we take it to the logical next step, and &lt;strong&gt;create an agent in Mini Micro&lt;/strong&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.amazonaws.com%2Fuploads%2Farticles%2Fwfsemryrv3042zq40a1v.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.amazonaws.com%2Fuploads%2Farticles%2Fwfsemryrv3042zq40a1v.png" alt="MiniClaw logo" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing MiniClaw
&lt;/h2&gt;

&lt;p&gt;Yesterday I sat down and created &lt;a href="https://github.com/JoeStrout/miniclaw" rel="noopener noreferrer"&gt;MiniClaw&lt;/a&gt;.  It consists mainly of three files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/JoeStrout/miniclaw/blob/main/instructions.txt" rel="noopener noreferrer"&gt;instructions.txt&lt;/a&gt;: these are the instructions to the LLM&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/JoeStrout/miniclaw/blob/main/agent.ms" rel="noopener noreferrer"&gt;agent.ms&lt;/a&gt;: the main program, which invokes the LLM and manages its context&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/JoeStrout/miniclaw/blob/main/tools.ms" rel="noopener noreferrer"&gt;tools.ms&lt;/a&gt;: code for the tools the agent can use to read, write, and manipulate files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So what can it do?  Well, MiniClaw can read any file accessible within Mini Micro, which means the &lt;code&gt;/sys&lt;/code&gt; disk, plus whatever minidisk or folder you have mounted as &lt;code&gt;/usr&lt;/code&gt; and &lt;code&gt;/usr2&lt;/code&gt;.  It can also write files (only) under &lt;code&gt;/usr/workspace&lt;/code&gt;.  So, similar to Claude Code or most other agents, you can use it to create and modify pretty much any kind of text file.  Or you can just ask it to explain and summarize things for you.  For example, I asked it "tell me about the pictures on the sys disk", and it wrote out a nice summary:&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.amazonaws.com%2Fuploads%2Farticles%2Fie14mbok8oxwe5pv97qc.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.amazonaws.com%2Fuploads%2Farticles%2Fie14mbok8oxwe5pv97qc.png" alt="Screen shot of /sys/pics summary" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On another occasion, I asked it to create a &lt;code&gt;.md&lt;/code&gt; (Markdown) file describing all the demos found in /sys/demo.  But then, in a later session, I decided that the document it created was too wordy, so I asked it to shorten it:&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.amazonaws.com%2Fuploads%2Farticles%2Fu9ycpkf1fyi66le5aftv.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.amazonaws.com%2Fuploads%2Farticles%2Fu9ycpkf1fyi66le5aftv.png" alt="Screen shot of agent shortening DEMO_GUIDE.md" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The gray text gives us some hints as to what the code is doing: it shows when we call the LLM, how much data we get back as a response, and what tool the LLM is using (and why).&lt;/p&gt;

&lt;p&gt;Some tasks, like this one, take only a couple of tool calls.  Others take more.  The LLM will keep invoking tools, occasionally printing some messages for us about its work, until it figures the task is complete (or that it's unable to complete it).&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The complete &lt;a href="https://github.com/JoeStrout/miniclaw/blob/main/agent.ms" rel="noopener noreferrer"&gt;agent.ms&lt;/a&gt; file is only 263 lines long, divided into 14 functions.  That's a bit too long to go over line by line here, but we'll hit the highlights, and I encourage you to check the source file for details.&lt;/p&gt;

&lt;p&gt;The big picture is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each time we call the LLM, we give it our instructions (always the same), and the prompt (varies each turn).&lt;/li&gt;
&lt;li&gt;The prompt includes messages from the user, previous tool calls made by the agent, and the results of those calls -- all this stuff is called the "history".  It also includes the current task, so the LLM is clear on what it's supposed to be doing.&lt;/li&gt;
&lt;li&gt;The LLM gives us a response in JSON format: either a tool call, a question for the user, an intermediate message, or a final message (indicating it's done).&lt;/li&gt;
&lt;li&gt;We run any tool calls the LLM has asked for, and append the call and results to the history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's pretty much it.  The main loop looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;currentUserInput&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"==&amp;gt; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"#00AA00"&lt;/span&gt;
            &lt;span class="n"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUserInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gray&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getResponse&lt;/span&gt;
        &lt;span class="n"&gt;respData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;respData&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="n"&gt;addToHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"**IMPORTANT:** You must format your response as a JSON object!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;handleResponse&lt;/span&gt; &lt;span class="n"&gt;respData&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;currentUserInput&lt;/code&gt; is the instruction the agent is working on; it's empty at the start of the run, or when the agent says it's finished.  So then we get more input from the user.  (Half the code above is just fiddling with the text color to be fancy.)&lt;/p&gt;

&lt;p&gt;Then we call &lt;code&gt;getResponse&lt;/code&gt; to get the LLM's response to the current context (instructions plus prompt as described above), and try to parse it as JSON.  Occasionally the LLM will forget to format its response as JSON; if that happens, we just add a stern reminder to the history (so the LLM will see it) and try again.  Otherwise, we call &lt;code&gt;handleResponse&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;handleResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;addToHistory&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;printNicely&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
        &lt;span class="n"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"question"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;printNicely&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
        &lt;span class="n"&gt;addToHistory&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--- User response ---"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--- End user response ---"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"finish"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;printNicely&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
        &lt;span class="n"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUserInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"tool_call"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;handleToolCall&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;addToHistory&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: invalid response type """&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
          &lt;span class="s2"&gt;"""; must be ""message"", ""question"", ""finish"", or ""tool_call""."&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've removed some of the error handling and text-coloring above for clarity, but this is the gist of it.  We just switch based on the &lt;code&gt;type&lt;/code&gt; of response we got from the LLM; it should be one of the four types we put in the instructions.  Again, note that when we want to give the LLM more information -- like the user's response to a question -- we just add it to the history.  &lt;/p&gt;

&lt;p&gt;Let's talk about that &lt;code&gt;addHistory&lt;/code&gt; method a moment.  Its job is mainly just to append the given string(s) to a list of strings, so they can be included in the context.  But for any agent, context management is very important!  Too much context burns through tokens, and degrades LLM performance.  So, our &lt;code&gt;addHistory&lt;/code&gt; method limits how much history it remembers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;addToHistory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;
    &lt;span class="n"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;historyLen&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;historyLen&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
        &lt;span class="n"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;historyLen&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;history&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="n"&gt;len&lt;/span&gt;
        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pull&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;discard&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Probably the next most important function is &lt;code&gt;promptInput&lt;/code&gt;, which calculates the "prompt" part of the context -- the part that varies from turn to turn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;promptInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
    &lt;span class="nf"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="s2"&gt;"# Task/User Input"&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="n"&gt;currentUserInput&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="s2"&gt;"# Current State"&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="s2"&gt;"Date/time: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="s2"&gt;"# Recent history"&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, right?  It's just composing a bit of Markdown calling out the current user input, the current state (which for this version of MiniClaw, is only the date/time), and the history.  This stuff is appended to the static &lt;a href="https://github.com/JoeStrout/miniclaw/blob/main/instructions.txt" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;, and sent to the LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools
&lt;/h2&gt;

&lt;p&gt;The functions above are going to be pretty standard for any agent.  What determines what the agent actually &lt;em&gt;does&lt;/em&gt; are the tools and instructions provided to it.  In MiniClaw, the tools are separated out into their own file, &lt;a href="https://github.com/JoeStrout/miniclaw/blob/main/tools.ms" rel="noopener noreferrer"&gt;tools.ms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This file begins with some little helper functions: &lt;code&gt;err&lt;/code&gt;, &lt;code&gt;errMissingArg&lt;/code&gt;, and &lt;code&gt;okResult&lt;/code&gt;, which all generate little result maps to be returned to the LLM; plus &lt;code&gt;resolvePath&lt;/code&gt; and &lt;code&gt;isWriteable&lt;/code&gt;, which help the tool code deal with files properly.  Then, it has a function for each tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;list_files&lt;/li&gt;
&lt;li&gt;read_file&lt;/li&gt;
&lt;li&gt;head_file&lt;/li&gt;
&lt;li&gt;tail_file&lt;/li&gt;
&lt;li&gt;write_file&lt;/li&gt;
&lt;li&gt;delete_file&lt;/li&gt;
&lt;li&gt;move_file&lt;/li&gt;
&lt;li&gt;make_dir&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these takes a map containing arguments (which we've gotten by parsing the JSON from the LLM), does its thing if it can, and then returns a map of results -- usually from one of the &lt;code&gt;err&lt;/code&gt; functions, or from &lt;code&gt;okResult&lt;/code&gt;, with details (like the path of the affected file) added in.  This lets the LLM know whether its attempt to use a tool was successful.&lt;/p&gt;

&lt;p&gt;As an example, let's look at &lt;code&gt;head_file&lt;/code&gt;, whose job it is to return the first so-many lines of a text file.  This tool is described in the instructions file as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"head_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"desc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Return the first n lines of a UTF-8 file.  Use this to examine large or unknown text files."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We give the agent the name of the tool, a description including advice on when to use it, and info on the expected arguments.  So, the actual MiniScript function is expecting its &lt;code&gt;args&lt;/code&gt; map to contain &lt;code&gt;"path"&lt;/code&gt; and &lt;code&gt;"lines"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;head_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errMissingArg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid path `"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"`"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lines"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;okResult&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It pulls those arguments out of the map, does some simple validation on them, reads the file, and returns the requested data as the "content" string of the result map.&lt;/p&gt;

&lt;p&gt;The other tools all work in a similar fashion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it out
&lt;/h2&gt;

&lt;p&gt;You can download the MiniClaw source files from &lt;a href="https://github.com/JoeStrout/miniclaw" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, but in order to access the LLM (gpt-5.4-nano) it uses, you'll need to set up an API key:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://platform.openai.com/" rel="noopener noreferrer"&gt;platform.openai.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;API Keys&lt;/strong&gt; on the left.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create new secret key&lt;/strong&gt;, give it a name like "MiniClaw", and copy the key it shows you.&lt;/li&gt;
&lt;li&gt;Paste that into a file called &lt;code&gt;api_key.secret&lt;/code&gt; next to &lt;code&gt;agent.ms&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then you can mount that directory in Mini Micro, and &lt;code&gt;run "agent"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As for cost, I wouldn't worry about it too much... I used this thing a &lt;em&gt;lot&lt;/em&gt; yesterday and today while developing it, and it cost under 40 cents.  &lt;code&gt;gpt-5.4-nano&lt;/code&gt; is pretty cheap, and seems smart enough for everything I've tried so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it further
&lt;/h2&gt;

&lt;p&gt;This is where it gets fun: &lt;strong&gt;add your own tools!&lt;/strong&gt;  This version of MiniClaw only does basic file creation/manipulation, as you can see from the tool list above.  But you could make your own MiniClaw do anything Mini Micro is capable of.  Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display a picture&lt;/li&gt;
&lt;li&gt;Play a sound (or series of sounds — making music?)&lt;/li&gt;
&lt;li&gt;Launch a program&lt;/li&gt;
&lt;li&gt;Access the web or web services via &lt;code&gt;http&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Do math&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/joestrout/combining-gpt-and-wolfram-alpha-ma2"&gt;Call Wolfram Alpha&lt;/a&gt; for help&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And adding more tools is pretty easy: just create a function for it in &lt;code&gt;tools.ms&lt;/code&gt;, and add a description of the tool to &lt;code&gt;instructions.txt&lt;/code&gt;.  That's it; the LLM should be smart enough to invoke it when the time is right.&lt;/p&gt;

&lt;p&gt;If you want to switch to a different LLM provider (here's a handy guide to &lt;a href="https://get-hermes.ai/models/" rel="noopener noreferrer"&gt;AI models for Hermes&lt;/a&gt;), you might have to adjust or rewrite the &lt;code&gt;getResponse&lt;/code&gt; function, which formats the input for the LLM and then digs the actual response text out of the JSON package it's buried in.  But this is totally doable.  You could even run a local LLM, if that's your thing, and connect to it at &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There's also a lot that could be done to improve MiniClaw's user interface.  Right now it just prints (albeit with pretty colors, supporting basic markdown commonly used by LLMs) stuff to the text display as it comes in.  You could instead make a structured display, keeping the current task up top, showing some info about the context on the side, and neatly formatted responses (perhaps drawn with proportional fonts into a PixelDisplay) below.&lt;/p&gt;

&lt;p&gt;My main goal with MiniClaw was to create an agent that is simple and small enough (and MiniScript enough!) to be easily understood and modified.  And the great thing is, Mini Micro is a safe sandbox environment, assuming you only mount minidisks or folders you aren't worried about.  So go nuts and have fun!&lt;/p&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>agents</category>
      <category>programming</category>
    </item>
    <item>
      <title>MiniScript Weekly News — Apr 9, 2026</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Thu, 09 Apr 2026 23:01:27 +0000</pubDate>
      <link>https://forem.com/joestrout/miniscript-weekly-news-apr-9-2027-456o</link>
      <guid>https://forem.com/joestrout/miniscript-weekly-news-apr-9-2027-456o</guid>
      <description>&lt;p&gt;&lt;em&gt;Correction: the headline originally said 2027... now corrected, before the temporal police could find me.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Updates
&lt;/h2&gt;

&lt;p&gt;MiniScript 2 saw a nice round of progress this week, with function metadata support being the big new feature. The new &lt;code&gt;info(@func)&lt;/code&gt; intrinsic can now expose a function’s name, note, and params: &lt;a href="https://github.com/JoeStrout/miniscript2" rel="noopener noreferrer"&gt;JoeStrout/miniscript2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the raylib side, Joe added &lt;code&gt;http.post&lt;/code&gt; support, plus &lt;code&gt;file.loadRaw&lt;/code&gt; and &lt;code&gt;file.saveRaw&lt;/code&gt; for binary data. That should make it easier to talk to REST services and handle non-text assets in &lt;a href="https://github.com/JoeStrout/raylib-miniscript" rel="noopener noreferrer"&gt;raylib-miniscript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The alchemy UI toolkit keeps taking shape too. Joe shared the first functional UI demo, and the dev log notes working progress on buttons, static text, spinner controls, and a coordinate-system-agnostic &lt;code&gt;Rect&lt;/code&gt; class: &lt;a href="https://github.com/JoeStrout/alchemy-ui" rel="noopener noreferrer"&gt;alchemy-ui&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Projects
&lt;/h2&gt;

&lt;p&gt;A big update for the Mini Micro showcase: Joe built a tool to scan itch.io pages and auto-fill missing entries, then expanded the catalog to 80 programs. If you’ve got a MiniScript or Mini Micro game or demo to share, he’s still asking for links to add to the catalog: &lt;a href="https://miniscript.org/MiniMicro/#programs" rel="noopener noreferrer"&gt;Mini Micro programs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Several community-made itch.io projects were highlighted this week. If you want to browse or share them, check out &lt;a href="https://dslower.itch.io/march-of-the-zomblez" rel="noopener noreferrer"&gt;March of the Zomblez&lt;/a&gt;, &lt;a href="https://dslower.itch.io/two-souls" rel="noopener noreferrer"&gt;Two Souls&lt;/a&gt;, and &lt;a href="https://dslower.itch.io/going-viral" rel="noopener noreferrer"&gt;Going Viral&lt;/a&gt;, all by &lt;a class="mentioned-user" href="https://dev.to/dslower"&gt;@dslower&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Other community submissions that came up include &lt;a href="https://dat-one-dev.itch.io/chaturanga" rel="noopener noreferrer"&gt;Chaturanga&lt;/a&gt;, and &lt;a href="https://dat-one-dev.itch.io/color-crash" rel="noopener noreferrer"&gt;Color Crash&lt;/a&gt; by @dat-one-dev, and &lt;a href="https://bibleclinger.itch.io/color-smash-tank" rel="noopener noreferrer"&gt;Color Smash Tank&lt;/a&gt;, and &lt;a href="https://bibleclinger.itch.io/space-fighter-85" rel="noopener noreferrer"&gt;Space Fighter 85&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/bibleclinger"&gt;@bibleclinger&lt;/a&gt;. It’s great to see so many jam entries and experiments getting some love.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion Highlights
&lt;/h2&gt;

&lt;p&gt;There was some fun brainstorming around a possible physical Mini Micro device. Ideas ranged from a compact keyboard (with arrow keys a must-have), to SD card storage, to even a rounded case with arcade buttons or bundled gamepads.&lt;/p&gt;

&lt;p&gt;In the MiniScript 2 forum discussion, Joe floated the new &lt;code&gt;info&lt;/code&gt; intrinsic before implementing it, and got a warm response. It’s a promising direction for making introspection and tooling much more pleasant in MS2: &lt;a href="https://forums.miniscript.org/d/618-accessing-function-metadata" rel="noopener noreferrer"&gt;forum thread&lt;/a&gt;.  There was also more discussion of a new &lt;a href="https://forums.miniscript.org/d/617-an-error-handling-proposal-for-miniscript-20/5" rel="noopener noreferrer"&gt;error handling system&lt;/a&gt;, which may see implementation next week — stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Game Jam Notes
&lt;/h2&gt;

&lt;p&gt;This week’s jam theme was “one light source,” and the community quickly pointed folks toward the &lt;code&gt;/sys/demo/2dVis&lt;/code&gt; demo and the &lt;a href="https://itch.io/jam/micro-jam-056" rel="noopener noreferrer"&gt;micro-jam-056&lt;/a&gt; page. Joe also pointed out Florian’s earlier gem, &lt;a href="https://florian-castel.itch.io/in-my-bubble" rel="noopener noreferrer"&gt;In My Bubble&lt;/a&gt;, as another brilliant example of what can be done with a focused idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the Community
&lt;/h2&gt;

&lt;p&gt;BibleClinger also kicked around an interesting idea for a community-authored MiniScript book, where experienced users could each contribute a chapter. That sounds like a wonderful way to share knowledge and celebrate the depth of the community.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and keep those MiniScript projects coming!&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming Game Jams
&lt;/h2&gt;

&lt;p&gt;These upcoming jams look like a great fit for Mini Micro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/impressions-composing-jam-season-4-melody-jam" rel="noopener noreferrer"&gt;Impressions Composing Jam Season 4: Melody Jam&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-10 22:00:00) — A focused music jam centered on crafting a memorable melody from a provided theme, with clear, simple requirements and plenty of room for creative interpretation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/kbgames-game-boys-chillin-on-a-grid-jam" rel="noopener noreferrer"&gt;KBGames "Game Boys Chillin' On A Grid" Jam&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-13 22:18:30) — A super flexible jam centered on Game Boy vibes, chill/cozy energy, and grid-based ideas—an excellent match for retro pixel art, tile grids, and simple 2D gameplay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/smashthefash" rel="noopener noreferrer"&gt;Games Transformed 2026 - 'Smash The Fash' Game Jam&lt;/a&gt;&lt;/strong&gt; — A politically charged jam inviting all kinds of antifascist games, from activist tactics and mutual aid to surreal puzzles and hopeful visions of solidarity, with room for serious, playful, or experimental takes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/dtj36-25" rel="noopener noreferrer"&gt;Devs That Jam 36-hour Challenge #25: Anniversary Edition&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-25 10:00:00) — A fast, beginner-friendly 36-hour jam with a community-chosen theme, flexible use of pre-made assets, and judging that rewards fun, clarity, visuals, audio, and polish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/panafrican2026" rel="noopener noreferrer"&gt;Pan-African Jam with Lagos Games Week 2026&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-23 22:00:00) — A highly welcoming, fully virtual jam centered on creativity, cross-border collaboration, and game discovery—open to any genre and especially appealing for developers who want a community-driven event with real visibility and prizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/portfolio-builders-jam-week-69" rel="noopener noreferrer"&gt;Portfolio Builders Jam - Week #69&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-20 11:00:00) — A very flexible weekly jam focused on building portfolio pieces, with room for playable demos, polished art, audio, or code showcases—ideal if you want to make something small, skill-focused, and retro-friendly.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>news</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Master Algorithm</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Tue, 07 Apr 2026 17:03:14 +0000</pubDate>
      <link>https://forem.com/joestrout/the-master-algorithm-2oie</link>
      <guid>https://forem.com/joestrout/the-master-algorithm-2oie</guid>
      <description>&lt;p&gt;In 2015, a book by AI researcher Pedro Domingos came out called &lt;a href="https://www.hachettebookgroup.com/titles/pedro-domingos/the-master-algorithm/9780465061921" rel="noopener noreferrer"&gt;&lt;em&gt;The Master Algorithm: How the Quest for the Ultimate Learning Machine Will Remake Our World&lt;/em&gt;&lt;/a&gt;.  The author explored the "five tribes" of artificial intelligence (AI), and how each one might develop into the "Master Algorithm" of intelligence itself — the algorithm that could learn to do virtually anything humans and other animals can learn to do.  The five tribes are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;inductive reasoning&lt;/li&gt;
&lt;li&gt;connectionism (aka neural networks)&lt;/li&gt;
&lt;li&gt;evolutionary computation&lt;/li&gt;
&lt;li&gt;Bayesian networks&lt;/li&gt;
&lt;li&gt;analogical modelling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was ten years ago.  It was very much not obvious, at that time, which of these — or what combination of them — or even whether something entirely different — might turn out to be the Master Algorithm.&lt;/p&gt;

&lt;p&gt;But it's clear now.  I'm calling it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Master Algorithm is neural networks.
&lt;/h2&gt;

&lt;p&gt;It turns out, the master algorithm is connectionism.  A neural network, when it's big enough, structured appropriately, and trained on enough data, can do it all: language, reasoning, translation, programming, answering questions, following instructions, understanding pictures and videos, &lt;em&gt;generating&lt;/em&gt; pictures and videos, solving complex math problems, and on and on.&lt;/p&gt;

&lt;p&gt;The goalpost-movers like to pick at each of those and say "yes, but, it doesn't do &lt;em&gt;this thing&lt;/em&gt; very well and &lt;em&gt;that thing&lt;/em&gt; doesn't really count because of the following hand-wavy reasons."  But if you read the book, it's obvious that everything we all take for granted nowadays was far-off science fiction in 2015.  AI researchers literally &lt;em&gt;dreamed&lt;/em&gt; of machines that could do half of what ChatGPT or Claude does before breakfast, or what &lt;a href="https://deepmind.google/models/gemma/" rel="noopener noreferrer"&gt;Gemma&lt;/a&gt; does running locally &lt;em&gt;on my cell phone&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The other four approaches?  They still have their uses in specialized cases, and they're still doing more or less what they were doing in 2015.  None of them turned out to be able to do pretty much everything, nor did any of them turn out to get a lot smarter and more capable when you simply scale them up.  (Indeed, most of them explode when you try to do that.)&lt;/p&gt;

&lt;h2&gt;
  
  
  This was not obvious.
&lt;/h2&gt;

&lt;p&gt;I've considered myself a "connectionist" since middle school, when I did my first neural networks science fair project.  (Fun fact: my junior year of high school, I went to the International Science &amp;amp; Engineering Fair with my novel algorithm for recursive neural networks, and won the U.S. Army's top prize and a 2-week trip to Japan... and I still practice Japanese every day today.)&lt;/p&gt;

&lt;p&gt;But I did not expect neural networks, on their own, to be the master algorithm.  Hardly anyone expected that.  Neural networks were very good at perception (identifying/classifying patterns in raw data), and at prediction (which is really the same thing).  But it was not at all clear how a neural network would ever be able to carry on a real conversation, reason through problems, etc.  I thought you'd probably need some combination of a neural network and symbolic (e.g. inductive) reasoning.&lt;/p&gt;

&lt;p&gt;I mean, of course our own brains are made entirely of neurons.  So as a raw proof of concept, yes, it was obvious that neural networks &lt;em&gt;could&lt;/em&gt; do it all.  But I assumed that evolution had equipped our brains with very specialized circuits for language, logic, &amp;amp; reasoning, and that you would not get these facilities out of a general-purpose neural network running some general-purpose learning algorithm.&lt;/p&gt;

&lt;p&gt;But nope.  It turns out that you do get exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment this happened.
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://mitpress.mit.edu/9780262049955/what-is-intelligence/" rel="noopener noreferrer"&gt;What Is Intelligence?&lt;/a&gt; (2025), Google AI researcher Blaise Agüera y Arcas writes about the moment that I would consider the pivotal turning point.  They were training a neural network to predict the next word after a bunch of previous words, so that they could make a better autocomplete function.  This is a natural and obvious application of neural networks' strengths, and because people are terrible (and terribly slow) at typing messages on cell phones, better autocomplete was worth investing in.&lt;/p&gt;

&lt;p&gt;But they had trained a very &lt;em&gt;large&lt;/em&gt; model.  On a very large amount of text.  And the model started talking to them.  They discovered that they could ask it questions, and it would answer.  The could even ask it things that had certainly never been in its training data — like, how to translate a specific made-up sentence from one language to another — and, with a bit of urging, it would do it, and do it correctly.&lt;/p&gt;

&lt;p&gt;I can only imagine the complex emotions they must have felt at that moment.  They realized what some deniers still haven't understood today: a sufficiently deep language model isn't just parroting its training data; it is &lt;em&gt;understanding&lt;/em&gt; and responding with real intelligence.&lt;/p&gt;

&lt;p&gt;Now, a raw language model, without additional reinforcement learning to shape its behavior, is rather schizophrenic; it slips in and out of different personalities, carries on both halves of a conversation all by itself, etc.  Few people get to interact with a network in this state, because (as Agüera y Arcas explains) it is quite disturbing.  But the behavioral training fixes that, improves reasoning skills, and gives us the AI coworkers we use daily today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prediction, all the way down
&lt;/h2&gt;

&lt;p&gt;So how is this possible?  How &lt;em&gt;does&lt;/em&gt; a neural network act as the master algorithm?&lt;/p&gt;

&lt;p&gt;It turns out, intelligence is fundamentally just prediction.  It's prediction at all levels: cortical columns predict what their next inputs are going to be; our visual system learns to predict what the world is going to look like in the next moment; our language system learns to predict the next word we are going to hear or say.  In a literal sense, LLMs like Claude do nothing but predict the next token, based on all the tokens in the session so far.&lt;/p&gt;

&lt;p&gt;But prediction is all you need.  When Claude generates a string of tokens working through a problem, it is not fundamentally different from the string of thoughts (mostly in the form of words) that you and I would generate working through a similar problem.  Reinforcement learning biases the network so that these strings of predictions tend to go in &lt;em&gt;useful&lt;/em&gt; directions, ones that were successful during training.  In humans (unlike current LLMs), training is an ongoing process, so we are constantly tweaking our own neural networks to make these trains of predictions more successful.  But fundamentally, moment to moment, it's still just predicting the next thing, at every level of the network.&lt;/p&gt;

&lt;p&gt;We now understand that some problems are what Agüera y Arcas calls &lt;strong&gt;AI complete&lt;/strong&gt;.  Solving an AI complete problem requires actually understanding the world that the problem represents; no shallower, surface-features sort of representation can do it.  Examples of AI complete problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next word prediction: it's impossible to predict the next word in a string of text with any accuracy unless you understand what those words actually mean.&lt;/li&gt;
&lt;li&gt;Language translation: ditto; you can't just look up individual words in a dictionary.&lt;/li&gt;
&lt;li&gt;Video next-frame prediction: requires understanding what the objects in the video are, how they behave, what gravity does, etc.&lt;/li&gt;
&lt;li&gt;Picture or video captioning: requires understanding both what the objects are, and how they are referred to in language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A sufficiently big neural network, given sufficient training data, will eventually solve almost any problem you give it.  So if you give it an AI complete problem, which can only be solved by understanding the world... it will understand the world.&lt;/p&gt;

&lt;p&gt;Is its understanding perfect?  Of course not.  But then, that's &lt;a href="https://spyscape.com/article/illuminati-qanon-lizard-people-and-other-conspiracy-theories" rel="noopener noreferrer"&gt;true of humans&lt;/a&gt;, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  So that's it for AI research
&lt;/h2&gt;

&lt;p&gt;Haha, just kidding!  LLMs are &lt;a href="https://www.noemamag.com/artificial-general-intelligence-is-already-here/" rel="noopener noreferrer"&gt;general intelligence&lt;/a&gt;, and already &lt;a href="https://scitechdaily.com/for-the-first-time-chatgpt-has-solved-an-unproven-math-problem-in-geometry/" rel="noopener noreferrer"&gt;smarter than humans&lt;/a&gt; in some ways.  But they have glaring limitations: in particular, "training" and "inference" are completely separated processes.  The LLMs we use every day do not learn from experience, except insofar as that experience can be crammed into the context window.  &lt;/p&gt;

&lt;p&gt;The addition of &lt;a href="https://cameronrwolfe.substack.com/p/rl-continual-learning" rel="noopener noreferrer"&gt;continual learning&lt;/a&gt; will allow them to get better at tasks with practice, just like we do.  But there are a lot of tricky open questions: how do you avoid forgetting too much old knowledge while acquiring new knowledge?  How do you keep the AI's personality stable over time?  And how do you even learn this new stuff quickly enough to matter?&lt;/p&gt;

&lt;p&gt;Moreover, there really &lt;em&gt;are&lt;/em&gt; things that neural networks are terrible at.  And ironically, those are exactly the things that most AI research has been doing for the last 80 years: game playing, deep search, complex logical reasoning, optimization, etc.  We (and other neural networks) can &lt;em&gt;approximately&lt;/em&gt; do those things, with a lot of training and practice, but there are classical (GOFAI or "Good Old-Fashioned AI") algorithms that do them much better and faster.  We'll continue to need progress on these, and on ways to integrate them with neural networks to get the best of both worlds.&lt;/p&gt;

&lt;p&gt;This is just a random sampling; in fact there are many, many open research directions.  We've cracked the code of intelligence; we know now what intelligence &lt;em&gt;is&lt;/em&gt; and how to produce it in a machine.  But that's only the tip of the iceberg.  It is the beginning of wisdom, not the end.&lt;/p&gt;

&lt;p&gt;The next 5 or 10 years are going to be very exciting.  Hold on tight!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>discuss</category>
      <category>science</category>
    </item>
    <item>
      <title>MiniScript Weekly News — Apr 1, 2026</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Thu, 02 Apr 2026 00:36:09 +0000</pubDate>
      <link>https://forem.com/joestrout/miniscript-weekly-news-apr-1-2026-7b1</link>
      <guid>https://forem.com/joestrout/miniscript-weekly-news-apr-1-2026-7b1</guid>
      <description>&lt;h2&gt;
  
  
  Development Updates
&lt;/h2&gt;

&lt;p&gt;Work on &lt;strong&gt;MiniScript 2&lt;/strong&gt; continues to pick up speed, and the team shared that a working &lt;strong&gt;REPL&lt;/strong&gt; is now in place in both C# and C++. The latest dev log also mentions a refactor to better preserve globals across REPL entries, plus a fix for multi-function REPL handling and Ctrl-D to exit.&lt;br&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/JoeStrout/miniscript2" rel="noopener noreferrer"&gt;miniscript2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;raylib-miniscript&lt;/strong&gt; side, there were a few useful updates landed this week: &lt;code&gt;resourceCounts&lt;/code&gt; now reports loaded resources, &lt;code&gt;FileHandle&lt;/code&gt; was added, and the text mutation intrinsics were refreshed with new &lt;code&gt;...Alloc&lt;/code&gt; variants. These changes should help with debugging leaks and keeping the bindings in step with newer raylib APIs.  &lt;/p&gt;

&lt;p&gt;An important change this week: raylib-miniscript now requires scripts to call &lt;code&gt;rl.InitWindow&lt;/code&gt; themselves instead of the engine doing it behind the scenes. Joe also added a direct MiniScript translation of the raylib high-DPI demo, which should make window setup clearer for developers who want to support high-res displays like the Apple Retina or MacBook display.&lt;br&gt;
GitHub: &lt;a href="https://github.com/JoeStrout/raylib-miniscript" rel="noopener noreferrer"&gt;raylib-miniscript&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Projects
&lt;/h2&gt;

&lt;p&gt;Joe shared a fresh tutorial on Dev.to: &lt;strong&gt;&lt;a href="https://dev.to/joestrout/create-a-maze-game-in-mini-micro-4j32"&gt;Create a Maze Game in Mini Micro&lt;/a&gt;&lt;/strong&gt;. It walks through turning the built-in maze demo into a custom game, and it’s a great example of how approachable Mini Micro development can be for new creators.&lt;/p&gt;

&lt;p&gt;Also in the Mini Micro ecosystem, &lt;strong&gt;Dat_One_Dev&lt;/strong&gt; highlighted the possibility of listing Mini Micro, Soda, and the upcoming Raylib project on a game-distribution site so community-made games can be more discoverable. That came up in the context of showcasing user projects like &lt;strong&gt;SPACE EVADERS&lt;/strong&gt; by community member DSlower — always exciting to see MiniScript games getting attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion Highlights
&lt;/h2&gt;

&lt;p&gt;There was a lively MiniScript 2 design discussion about language minimalism, type hints, and the role of &lt;code&gt;@&lt;/code&gt; in function references. Joe reaffirmed the project’s “mini” philosophy while also noting that assertions can already help catch errors today, and might even inform future optimizations.  But he also outlined a proposal for a more extensive error-handling system based on a new "error" data type, with strict rules about how it interacts with other types.&lt;/p&gt;

&lt;p&gt;The community also brainstormed about broader tooling and extensibility, including ideas for terminal color customization, configurable prompts, and even an eventual FFI-style integration for things like Ollama or other external libraries. Joe’s response was encouraging: it’s a neat direction for “eventually,” though beyond the language core for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini Micro &amp;amp; Raylib Chatter
&lt;/h2&gt;

&lt;p&gt;A few Raylib-related debugging threads were especially productive this week. The team tracked down an HDPI issue to window initialization order, and Joe wrapped up the fix by moving the config flag setup earlier and clarifying that scripts should initialize the window themselves.&lt;/p&gt;

&lt;p&gt;There was also some good old-fashioned collaborative troubleshooting on the breakout demo and audio init behavior, with fixes landing for Windows build issues and asset handling along the way. It’s great to see community members jumping in with bug reports, PRs, and real-world testing.&lt;/p&gt;

&lt;p&gt;Until next week, happy scripting!&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming Game Jams
&lt;/h2&gt;

&lt;p&gt;These upcoming jams look like a great fit for Mini Micro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/pixelforge" rel="noopener noreferrer"&gt;Pixel Forge JAM 2026 [PRIZES]&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-18 07:30:00) — A strong fit for retro, pixel-art, or tile-based ideas, with a 7-day format that encourages fast, creative experimentation and accessible gameplay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/micro-jam-057" rel="noopener noreferrer"&gt;Micro Jam 057: Symmetry ($400 Prizes)&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-18 01:00:00) — A strong fit for 2D pixel, tile, or text-driven ideas, with the symmetry theme inviting clever puzzle, action, or visual-design twists and a forgiving jam format that’s easy to jump into.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/smashthefash" rel="noopener noreferrer"&gt;Games Transformed 2026 - 'Smash The Fash' Game Jam&lt;/a&gt;&lt;/strong&gt; — A politically charged jam inviting all kinds of antifascist games, from activist tactics and mutual aid to surreal puzzles and hopeful visions of solidarity, with room for serious, playful, or experimental takes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/reagent-game-jam-2026" rel="noopener noreferrer"&gt;Reagent Game Jam 2026&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-10 07:00:00) — An accessible beginner-friendly jam with lots of freedom in tools and style, making it a great chance to build a creative 2D game in a low-pressure setting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/gaminglejam" rel="noopener noreferrer"&gt;GamingleJam&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-10 17:14:37) — A low-pressure, any-engine jam that encourages collaboration and welcomes solo creators too, making it a great excuse to build a stylish 2D game and maybe team up with artists, musicians, or writers along the way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/avantbeetles-protest-games-jam" rel="noopener noreferrer"&gt;AvantBeetle's Protest Games Jam&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-04 04:00:00) — An open-ended protest-themed jam that welcomes experimental, symbolic, and small-scale games, making it a great chance to turn a strong message into a creative playable statement.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>news</category>
    </item>
    <item>
      <title>Create a Maze Game in Mini Micro</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Wed, 01 Apr 2026 18:17:19 +0000</pubDate>
      <link>https://forem.com/joestrout/create-a-maze-game-in-mini-micro-4j32</link>
      <guid>https://forem.com/joestrout/create-a-maze-game-in-mini-micro-4j32</guid>
      <description>&lt;p&gt;In the classic arcade days, there were a lot of maze games.  That's because mazes are &lt;em&gt;fun!&lt;/em&gt;  They give the player interesting choices at every point, and force you to look and plan globally while also paying attention to local hazards and opportunities.&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.amazonaws.com%2Fuploads%2Farticles%2F9z79nqu3difu85jnh1q0.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.amazonaws.com%2Fuploads%2Farticles%2F9z79nqu3difu85jnh1q0.png" alt="Maze game screen shots" width="800" height="781"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Clockwise from top left: Pac-Man (1980), Wizard of Wor (1980), Nibbler (1982), Mouse Trap (1981).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You don't see this as much in modern games.  A 3D shooter or adventure game might be in a maze, but with a first-person view, you miss out on that global planning because you can only see what's directly in front of you.&lt;/p&gt;

&lt;p&gt;All of which is to say, &lt;strong&gt;people should make more maze games.&lt;/strong&gt;  And &lt;a href="https://miniscript.org/MiniMicro" rel="noopener noreferrer"&gt;Mini Micro&lt;/a&gt; is a great environment for making them.  Let's make one right now!&lt;/p&gt;
&lt;h2&gt;
  
  
  Start with the maze demo
&lt;/h2&gt;

&lt;p&gt;Mini Micro has a handy demo of displaying and editing a maze at &lt;code&gt;/sys/demo/maze&lt;/code&gt;.  Start by running that.  You can run it online &lt;a href="https://miniscript.org/MiniMicro/index.html?cmd=run%20%22%2Fsys%2Fdemo%2Fmaze%22" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but better to &lt;a href="https://miniscript.org/MiniMicro/index.html#download" rel="noopener noreferrer"&gt;download&lt;/a&gt; Mini Micro if you don't have it already, and then just type &lt;code&gt;run "/sys/demo/maze"&lt;/code&gt; and press Enter.  The demo looks like this:&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.amazonaws.com%2Fuploads%2Farticles%2Fs7kxovlmfxg83oxjvrgx.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.amazonaws.com%2Fuploads%2Farticles%2Fs7kxovlmfxg83oxjvrgx.png" alt="Screen shot of /sys/demo/maze in Mini Micro" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can click and drag with the mouse to edit the maze.  It's important to understand that you're adding and deleting &lt;em&gt;wall corners&lt;/em&gt;, not walls or rooms.  These are the intersections of two (potential or actual) walls &lt;em&gt;between&lt;/em&gt; rooms.&lt;/p&gt;

&lt;p&gt;An important thing to understand is how what you see relates to the actual &lt;a href="https://miniscript.org/wiki/TileDisplay" rel="noopener noreferrer"&gt;TileDisplay&lt;/a&gt; creating it.  The best way to get that understanding is to press G in the demo, which toggles a visible grid (by shrinking the cells and spacing them out slightly).&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.amazonaws.com%2Fuploads%2Farticles%2Fj1tlypp099h92yu5n15q.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.amazonaws.com%2Fuploads%2Farticles%2Fj1tlypp099h92yu5n15q.png" alt="Screen shot of image with grid" width="514" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are Wang 2-corner tiles, which are explained in &lt;a href="https://dev.to/joestrout/wang-2-corner-tiles-544k"&gt;this post&lt;/a&gt;, so I won't go into detail here.  But they're very neat and easy once you get the hang of them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Start your program!
&lt;/h2&gt;

&lt;p&gt;Now let's leverage the code in this demo to make our own maze game.  If you were running the demo on the web, now you really should &lt;a href="https://miniscript.org/MiniMicro/index.html#download" rel="noopener noreferrer"&gt;download&lt;/a&gt; it so that you can save your work.&lt;/p&gt;

&lt;p&gt;We're going to use the &lt;em&gt;build a little, test a little&lt;/em&gt; approach here, building up the program bit by bit.  This is a great habit to have in all your coding; it's much easier and more successful than trying to write out an entire program at once.&lt;/p&gt;

&lt;p&gt;So if you're still in the demo, exit out of it by pressing &lt;code&gt;Esc&lt;/code&gt;, then type &lt;code&gt;clear&lt;/code&gt; and &lt;code&gt;reset&lt;/code&gt; to clear the screen and program.  Then &lt;code&gt;edit&lt;/code&gt;, and type or paste in this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clear

if env.importPaths.indexOf("/sys/demo") == null then
    env.importPaths.push "/sys/demo"
end if
import "maze"

maze.generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This clears the screen, and then checks whether our import paths already include "/sys/demo".  That's not normally a directory that &lt;code&gt;import&lt;/code&gt; searches, but it's where &lt;code&gt;maze.ms&lt;/code&gt; lives, so we need it to check there.  Then we &lt;code&gt;import "maze"&lt;/code&gt;, which loads the code from maze.ms into a map called &lt;code&gt;maze&lt;/code&gt; in our program.  Finally, we call &lt;code&gt;maze.generate&lt;/code&gt; to make and display a random maze, just like the demo.&lt;/p&gt;

&lt;p&gt;Save this program (I called mine "mouseMaze", for reasons that will become apparent), and then run it.  You should see a maze appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the maze
&lt;/h2&gt;

&lt;p&gt;The maze generated so far is a "pure" maze, i.e., one with exactly one path between any two points.  That's not actually much fun to play in; we want the player to have choices.  You could manually edit your maze layout, save it to disk in some way, and load it into your game; this would be most similar to what the classic games above did.  But for now, let's just knock out a random selection of walls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tiles = maze.tiles
cellSize = tiles.cellSize
extent = tiles.extent
maxCol = (extent[0] / 2) - 1
maxRow = (extent[1] / 2) - 1
tiles.setCellTint range(extent[0]-1), range(extent[1]-1), "#44FF44"

// Delete some extra walls so it's not a "pure" maze
for i in range(20)
    room = maze.rooms[rnd*maze.Room.cols][rnd*maze.Room.rows]
    if rnd &amp;lt; 0.5 then wall = "north" else wall = "east"
    room[wall] = false
end for
maze.setCornersFromRooms
maze.updateTiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;edit&lt;/code&gt; your program and add the above code, below what you wrote before.  This code begins by copying &lt;code&gt;maze.tiles&lt;/code&gt; into a &lt;code&gt;tiles&lt;/code&gt; variable, just to make it easier to refer to.  Then we also copy &lt;code&gt;cellSize&lt;/code&gt; and &lt;code&gt;extent&lt;/code&gt; out of that.  We also compute the &lt;code&gt;maxRow&lt;/code&gt; and &lt;code&gt;maxCol&lt;/code&gt; of rooms (rooms are the little spaces between the walls — there are only half as many rooms as walls in any direction).  Then we use &lt;code&gt;tiles.setCellTint&lt;/code&gt; to color the maze green.&lt;/p&gt;

&lt;p&gt;Then, the &lt;code&gt;for&lt;/code&gt; loop repeatedly selects a random room, and sets either the north or east wall to &lt;code&gt;false&lt;/code&gt;, removing it.  The code in maze.ms defines rooms this way, each room only responsible for the walls to its north and east side.  How would you be expected to know this?  Why, by reading the maze.ms code, of course!  (Or by reading this blog post.)&lt;/p&gt;

&lt;p&gt;Once the rooms have been edited, we call &lt;code&gt;maze.setCornersFromRooms&lt;/code&gt; and &lt;code&gt;maze.updateTiles&lt;/code&gt; to update the actual display on screen.&lt;/p&gt;

&lt;p&gt;Run the program again, and you should see a green maze with extra missing walls (sometimes even producing a dot, where a corner isn't attached to any walls at all).&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.amazonaws.com%2Fuploads%2Farticles%2Foanf6ykye8ud7orm0igh.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.amazonaws.com%2Fuploads%2Farticles%2Foanf6ykye8ud7orm0igh.png" alt="Screen shot of program result at this point" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a MazeSprite class
&lt;/h2&gt;

&lt;p&gt;Now to make this maze into a game, we need some sprites to move around in it.  And we want those things to move only in valid ways, i.e. not through walls.  We'll use the same Room data structure that the maze demo already defined for creating the maze, to determine where sprites can move.&lt;/p&gt;

&lt;p&gt;So, &lt;code&gt;edit&lt;/code&gt; your program again, and append this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MazeSprite = new Sprite
MazeSprite.col = 0
MazeSprite.row = 0
MazeSprite.goToRoom = function(roomCol, roomRow)
    self.col = roomCol; self.row = roomRow
    self.x = cellSize * (roomCol * 2 + 1)
    self.y = cellSize * (roomRow * 2 + 1)
end function

MazeSprite.canMove = function(dx, dy)
    col = self.col; row = self.row
    if dx and dy then return false
    if not 0 &amp;lt;= round(col + dx) &amp;lt;= maxCol then return false
    if not 0 &amp;lt;= round(row + dy) &amp;lt;= maxRow then return false
    if dx &amp;lt; 0 then return not maze.rooms[self.col-1][self.row].east
    if dx &amp;gt; 0 then return not maze.rooms[self.col][self.row].east
    if dy &amp;lt; 0 then return not maze.rooms[self.col][self.row-1].north
    if dy &amp;gt; 0 then return not maze.rooms[self.col][self.row].north
end function

MazeSprite.move = function(dx, dy)
    if not self.canMove(dx, dy) then return
    col = self.col; row = self.row
    for t in range(0, 1, 0.1)
        self.goToRoom col + dx*t, row + dy*t
        yield
    end for
    self.goToRoom round(self.col), round(self.row)
end function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a Sprite subclass called MazeSprite.  A MazeSprite knows what room column and row it's in, and in the &lt;code&gt;goToRoom&lt;/code&gt; method, it also converts those to screen coordinates.  Then there's the &lt;code&gt;canMove&lt;/code&gt; method, which determines whether the sprite can move in a given direction.  This includes bounds checks, as well as checking the appropriate wall.&lt;/p&gt;

&lt;p&gt;Finally, we have a &lt;code&gt;move&lt;/code&gt; method that, if the sprite is able to move in the given direction at all, does so smoothly over 10 frames.  (In a full game you might need a fancier approach that does not block the rest of the game while moving -- but for today's game, there is only one sprite moving at a time, so this works fine.)&lt;/p&gt;

&lt;p&gt;You can run your program again at this point, just to ensure you haven't made any typos.  But if everything is good, it won't look any different from before.  We've defined the MazeSprite class, but we're not actually doing anything with it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the sprites
&lt;/h2&gt;

&lt;p&gt;It's time to add our characters to the maze!  I chose to make my game about a mouse questing for cheese, but feel free to explore the pictures available in /sys/pics and make it a bunny chasing carrots, or whatever you like.  (The &lt;code&gt;findFile&lt;/code&gt; command is a great way to explore!)&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;edit&lt;/code&gt; your program again, and add this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;player = new MazeSprite
player.image = file.loadImage("/sys/pics/animals/mouse.png")
player.scale = 1/3
player.goToRoom 0,0
display(4).sprites.push player

cheese = new MazeSprite
cheese.image = file.loadImage("/sys/pics/food/Cheese.png")
cheese.scale = 1/2
cheese.goToRoom round(rnd*maxCol), round(rnd*maxRow)
display(4).sprites.push cheese
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty straightforward: we create a player sprite in room 0,0, and a cheese sprite in some random room.  Both sprites are scaled down in order to fit into the maze.  Run your program now, and you should see these new additions to the game.  But the program still exits immediately to the blinking cursor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main loop
&lt;/h2&gt;

&lt;p&gt;We're almost done!  Let's just load a sound to play when the mouse gets the cheese, and then add the main loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gotSound = file.loadSound("/sys/sounds/munch.wav")

while not key.pressed("escape")
    yield
    dx = sign(round(key.axis("Horizontal", false)))
    dy = sign(round(key.axis("Vertical", false)))
    if not dx and not dy then continue
    player.move dx, dy
    if player.row == cheese.row and player.col == cheese.col then
        cheese.goToRoom round(rnd*maxCol), round(rnd*maxRow)
        gotSound.play
    end if
end while
key.clear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our main loop here is pretty simple.  We start by getting the horizontal (&lt;code&gt;dx&lt;/code&gt;) and vertical (&lt;code&gt;dy&lt;/code&gt;) axis inputs.  Using &lt;code&gt;key.axis&lt;/code&gt; allows the player to use arrow keys, WASD, or even a game pad.  We &lt;code&gt;round&lt;/code&gt; that and take the &lt;code&gt;sign&lt;/code&gt; so that we get clean input values of -1, 0, or 1 for each axis.  And if we don't have any input, we just &lt;code&gt;continue&lt;/code&gt; at the top of the loop (where we included a &lt;code&gt;yield&lt;/code&gt;, as all good main loops do).&lt;/p&gt;

&lt;p&gt;When we &lt;em&gt;do&lt;/em&gt; have an input, then we call &lt;code&gt;player.move&lt;/code&gt;, and then check whether the player is now in the same location as the cheese.  When that's the case, we play the &lt;em&gt;munch&lt;/em&gt; sound and teleport the cheese to a different room.&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.amazonaws.com%2Fuploads%2Farticles%2F6fjezubvf5of2qi85pv4.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.amazonaws.com%2Fuploads%2Farticles%2F6fjezubvf5of2qi85pv4.png" alt="Screen shot of the final game" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it!&lt;/strong&gt;  It's such a simple game, and yet it is immediately engaging.  You will see how you could build on this in all sorts of ways: make the goal move around, add enemies, add some sort of shooting, provide doors that can open or close or pivot, add sub-goals like visiting every part of the maze, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comments?  Questions?
&lt;/h2&gt;

&lt;p&gt;What was your favorite maze game?  What sort of maze game would you like to see in Mini Micro?  And what's stopping you from creating that game right now?  Share your thoughts in the comments below!&lt;/p&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>programming</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Parameter Objects in MiniScript</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Wed, 25 Mar 2026 15:44:23 +0000</pubDate>
      <link>https://forem.com/joestrout/parameter-objects-in-miniscript-358j</link>
      <guid>https://forem.com/joestrout/parameter-objects-in-miniscript-358j</guid>
      <description>&lt;p&gt;Sometimes a function starts out with a reasonable number of parameters, but as the code grows and more features are added, it ends up with more and more — like &lt;a href="https://miniscript.org/wiki/PixelDisplay.drawImage" rel="noopener noreferrer"&gt;PixelDisplay.drawImage&lt;/a&gt; in &lt;a href="https://miniscript.org/MiniMicro" rel="noopener noreferrer"&gt;Mini Micro&lt;/a&gt;, which has 10 parameters.&lt;/p&gt;

&lt;p&gt;Arguments in a function call in MiniScript are passed by position, not by name, which means that even if some of those parameters are optional, you have to specify all arguments that come before one you actually care about.  And of course you have to remember what order they're in.  (So many times I have typed &lt;code&gt;@gfx.saveImage&lt;/code&gt; at the Mini Micro prompt just to remind myself of the order of the parameters!)&lt;/p&gt;

&lt;p&gt;
  Why not passing parameters by name is a &lt;em&gt;good&lt;/em&gt; thing
  &lt;blockquote&gt;
&lt;p&gt;Languages that support passing parameters by name encourage developers to make functions with &lt;em&gt;lots&lt;/em&gt; of parameters.  And then it will turn out that only certain combinations of those parameters make sense, and the function sprouts pages of parameter-checking logic just to ensure that the user is calling it properly, and more pages of documentation or comments to help them do so.&lt;/p&gt;

&lt;p&gt;This is not just academic; I had to work with &lt;a href="https://github.com/ZettaAI/zetta_utils/blob/1992f681e2ec0bdf324ba54b971a278ef621e936/zetta_utils/layer/volumetric/cloudvol/build.py#L23" rel="noopener noreferrer"&gt;this 25-parameter monster&lt;/a&gt; for a while, and it was a nightmare.&lt;/p&gt;

&lt;p&gt;I have rarely seen any language feature so quickly lead to bad code as named parameters; it may even take the lead from GOTO in this department.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;p&gt;What can be done when you find yourself needing more than a handful of parameters?  Or, what if it's only a handful, but you find yourself frequently passing the same values from one function to another (helper methods for example)?  There is a standard solution: the &lt;a href="https://www.innovate.uc.edu/coding/refactorings/introduce-parameter-object/" rel="noopener noreferrer"&gt;&lt;em&gt;Introduce Parameter Object&lt;/em&gt; refactoring&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduce Parameter Object
&lt;/h2&gt;

&lt;p&gt;The idea here is simple: take that big batch of parameters you need, and replace them with a single object containing the same information.&lt;/p&gt;

&lt;p&gt;As an example, the &lt;code&gt;PixelDisplay.drawImage&lt;/code&gt; function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FUNCTION(self, image, left=0, bottom=0, width=-1, height=-1, srcLeft
=0, srcBottom=0, srcWidth=-1, srcHeight=-1, tint="#FFFFFF")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not counting &lt;code&gt;self&lt;/code&gt; (which is just whatever object comes before the dot when you call it), that's 10 parameters.  You need all 10 if you're going to tint the image, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;img = file.loadImage("/sys/pics/Block.png")
gfx.drawImage img, 500, 200, -1, -1, 0, 0, -1, -1, color.aqua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(I encourage you to fire up Mini Micro, either locally or &lt;a href="https://miniscript.org/MiniMicro/#playnow" rel="noopener noreferrer"&gt;on the web&lt;/a&gt;, and follow along for yourself.)&lt;/p&gt;

&lt;p&gt;Let's make a parameter-object version of this method.  (In your own code, you could actually replace the original method with the refactored one; but since you can't change the Mini Micro API, let's just make a new method.)  Use the &lt;code&gt;edit&lt;/code&gt; command and enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "mapUtil" // for map.get
drawImage = function(params)
    g = params.get("gfx", gfx)
    g.drawImage params.image,
      params.get("left", 0),
      params.get("bottom", 0),
      params.get("width", -1),
      params.get("height", -1),
      params.get("srcLeft", 0),
      params.get("srcBottom", 0),
      params.get("srcWidth", -1),
      params.get("srcHeight", -1),
      params.get("tint", "#FFFFFF")
end function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Close the editor and &lt;code&gt;run&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Now we have a &lt;code&gt;drawImage&lt;/code&gt; method that takes a single parameter.  You can call it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;params = {}
params.image = img
params.left = 500
params.bottom = 200
params.tint = color.aqua
drawImage params
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how this time, we only needed to specify the parameters that differ from the defaults.  We could also call it using a map literal, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drawImage {"image":img, "left":600, "bottom":200, "tint": color.pink}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works too.  Either way, the calling code is more clear, and the function interface is simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output Parameters
&lt;/h2&gt;

&lt;p&gt;There's a related pattern that is helpful when you need to (perhaps optionally) provide some extra detail on the result of a function call, and that is to use an &lt;em&gt;output&lt;/em&gt; parameter object.&lt;/p&gt;

&lt;p&gt;To make it more concrete, let's suppose you make a method that loads an image... but you want it to do some extra sanity checks, and report details on what it found to callers who care.  You could write it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "mapUtil" // for map.get
loadImage = function(path, outResults)
    r = outResults  // (shorthand)
    if r != null then
        r.path = path
        r.size = 0
        r.err = ""
    end if
    info = file.info(path)
    if info == null then
        if r then r.err = "File not found"
        return null
    end if
    if r then r.size = info.size
    directory = file.parent(path)
    name = file.name(path)
    if name[-4:] != ".png" and name[-4:] != ".jpg" then
        if r then r.err = "Path does not end with .png or .jpg"
        return null
    end if
    if file.children(directory).indexOf(name) == null then
        if r then r.err = "Case mismatch"
        return null
    end if
    return file.loadImage(path)
end function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;loadImage&lt;/code&gt; method takes the path to the function to load, but optionally, it also takes a map into which we will place some details: the path, the file size, and an error string.&lt;/p&gt;

&lt;p&gt;So callers who don't care about the details can just do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;img = loadImage("/sys/pics/Wumpus.png")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and they will get back the image or &lt;code&gt;null&lt;/code&gt;, but with no way of knowing why.  More careful callers could instead do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;result = {}
img = loadImage("/sys/pics/Wampus.png", result)
if not img then print "Couldn't load " + result.path +
  " because: " + result.err
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And (because I mistyped the image name) this will print "Couldn't load /sys/pics/Wampus.png because: File not found".&lt;/p&gt;

&lt;p&gt;(We might make use of exactly this pattern in some Mini Micro 2 APIs; see &lt;a href="https://github.com/JoeStrout/MiniMicro2/issues/1" rel="noopener noreferrer"&gt;this feature request&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Over Do It!
&lt;/h2&gt;

&lt;p&gt;There are two drawbacks to introducing parameter objects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They make calling the function more of a hassle for simple cases.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;drawImage img&lt;/code&gt; is easier than &lt;code&gt;drawImage {"image":img}&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They encourage parameter proliferation, just like named parameters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't create beasts with 20 or 30 parameters, even with this refactoring.  Instead consider smaller, more focused methods that do only one thing in one way.  Or at the very least, add wrapper methods that make the common use cases easy.&lt;/p&gt;

&lt;p&gt;But with those caveats in mind, parameter objects (including &lt;em&gt;output&lt;/em&gt; parameter objects) are a powerful tool in your toolbox.  Happy coding!&lt;/p&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>refactoring</category>
      <category>programming</category>
    </item>
    <item>
      <title>MiniScript Weekly News — March 24, 2026</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Tue, 24 Mar 2026 21:32:26 +0000</pubDate>
      <link>https://forem.com/joestrout/miniscript-weekly-news-march-24-2026-1jlj</link>
      <guid>https://forem.com/joestrout/miniscript-weekly-news-march-24-2026-1jlj</guid>
      <description>&lt;p&gt;&lt;em&gt;Editor's Note: I've developed an AI agent to help me gather and write this weekly newsletter, keeping you informed while still giving me time to work on things like MiniScript 2.0.  I hope you enjoy it!  -- Joe&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Updates
&lt;/h2&gt;

&lt;p&gt;Joe has launched a MiniScript News bot to help gather highlights from GitHub, Discord, forums, itch.io, and more, with the goal of making it easier to follow the growing community. That should be a big help as MiniScript activity spreads across so many places.&lt;/p&gt;

&lt;p&gt;MiniScript 2 continues to move forward with solid progress on performance and language/runtime cleanup. Recent work included adding &lt;code&gt;wait&lt;/code&gt; and &lt;code&gt;yield&lt;/code&gt;, refactoring the intrinsic API toward MiniScript 1 compatibility, committing to insertion-order maps, and making good headway toward a REPL and broader usability: &lt;a href="https://github.com/JoeStrout/miniscript2/blob/main/notes/DEV_LOG.md" rel="noopener noreferrer"&gt;miniScript2 dev log&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the Raylib side, &lt;code&gt;raylib-miniscript&lt;/code&gt; picked up several useful upgrades, including &lt;code&gt;run&lt;/code&gt;, improved &lt;code&gt;import&lt;/code&gt; path handling, &lt;code&gt;exit&lt;/code&gt;, more low-level APIs, shader demos, 3D model support, and full API docs. If you’re following the new foundation for Mini Micro 2 / Soda, there’s lots to explore: &lt;a href="https://github.com/JoeStrout/raylib-miniscript" rel="noopener noreferrer"&gt;raylib-miniscript&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Projects
&lt;/h2&gt;

&lt;p&gt;Trey Tomes has been sharing a lot of exciting Mini Micro work, including a roguelike/world-generation project, a VS Code extension that can execute MiniScript, and a MiniScript-based Perlin noise library. If you’re building in MiniScript, these are great examples of practical tools and ambitious gameplay systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/treytomes/miniscript-perlin-noise" rel="noopener noreferrer"&gt;miniScript Perlin Noise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/treytomes/micro-critters/blob/main/src/World.ms" rel="noopener noreferrer"&gt;micro-critters world demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/treytomes/micro-hack" rel="noopener noreferrer"&gt;micro-hack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also from the community, Blanket shared a work-in-progress Mini Micro file manager, with a very sensible warning to back up your files before trying anything risky. It’s always fun to see tools that help make the ecosystem more usable.&lt;/p&gt;

&lt;p&gt;A special shout-out to &lt;strong&gt;lagoon&lt;/strong&gt;, who showed up with a MiniScript-written Lisp implementation, &lt;a href="https://github.com/lattiahirvio/Clojette" rel="noopener noreferrer"&gt;Clojette&lt;/a&gt;. That’s exactly the kind of delightfully unexpected project that makes this community so fun.&lt;/p&gt;

&lt;p&gt;Finally, there were a couple of cool new game jam games in the last week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dslower.itch.io/march-of-the-zomblez" rel="noopener noreferrer"&gt;March of the Zomblez by DSlower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bleapew.itch.io/angels-dont-blink" rel="noopener noreferrer"&gt;Angels: Don't Blink by Bleapew&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discussion Highlights
&lt;/h2&gt;

&lt;p&gt;A big theme this week was input, layout, and UI design. Joe sketched out a promising layout system based on choosing two positioning constraints and using formulas for the rest, and the discussion around it drew a lot of enthusiasm from others who see how useful it could be for both game UI and desktop-style tools.&lt;/p&gt;

&lt;p&gt;There was also a lively thread about MiniScript 2 equality semantics, including the idea of an “approximately equal” operator. The consensus leaned toward keeping that logic in a function rather than baking too much ambiguity into the language, which feels very in the MiniScript spirit.&lt;/p&gt;

&lt;p&gt;On the Mini Micro side, there was a helpful reminder that tight loops need &lt;code&gt;yield&lt;/code&gt;, and a few useful tips for input handling and file creation. Community members kept helping one another troubleshoot, prototype, and think through the best patterns — a great sign of a healthy, collaborative space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning &amp;amp; Writing
&lt;/h2&gt;

&lt;p&gt;Kartik Patel published two more chapters of the “Zero To Game Dev” series, introducing Mini Micro and the first line of MiniScript in a beginner-friendly way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/kartik_patel/zero-to-game-dev-understanding-mini-micro-k8p"&gt;Understanding Mini Micro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/kartik_patel/zero-to-game-dev-first-line-of-code-5efk"&gt;First Line Of Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s great to see more approachable learning material appearing for newcomers. Posts like these make MiniScript feel even more welcoming to beginners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign-off
&lt;/h2&gt;

&lt;p&gt;Lots of steady progress, lots of creative experiments, and plenty of helpful back-and-forth this week — exactly what makes the MiniScript community special. Thanks to everyone building, testing, sharing, and cheering each other on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming Game Jams
&lt;/h2&gt;

&lt;p&gt;These upcoming jams look like a great fit for Mini Micro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/gone-fishing-game-jam" rel="noopener noreferrer"&gt;Gone Fishing Game Jam&lt;/a&gt;&lt;/strong&gt; (starts 2026-03-27 12:00:00) — A flexible, theme-light jam centered on fishing that leaves plenty of room for clever interpretations, from cozy pixel-art outings to quirky text adventures or tabletop-style ideas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/jerbobs-silly-game-jam" rel="noopener noreferrer"&gt;Jerbob's SILLY JAM&lt;/a&gt;&lt;/strong&gt; (starts 2026-03-27 13:00:00) — A free-form chaos jam that celebrates weird, silly, low-budget creativity—perfect for making something absurd, experimental, and charmingly janky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/aristos-interactive-jam-2" rel="noopener noreferrer"&gt;Aristos Interactive Jam #2&lt;/a&gt;&lt;/strong&gt; (starts 2026-03-27 21:00:00) — A beginner-friendly weekend jam focused on finishing a fun, playable game, with browser play required but no engine restrictions and plenty of freedom to use premade assets or AI support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/jam-chula-i" rel="noopener noreferrer"&gt;Jam Chula I&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-01 10:01:00) — A creative, inclusive art jam centered on the evocative theme of Hell, with complete freedom in format and plenty of room for stylized 2D games, interactive fiction, or other expressive retro-friendly ideas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/a-century-of-spells-a-fantasy-jam" rel="noopener noreferrer"&gt;A Century of Spells: A Fantasy Jam&lt;/a&gt;&lt;/strong&gt; (starts 2026-03-07 20:00:00) — A thoughtful fantasy jam centered on magic, memory, and the passage of time, with a strong emphasis on emotional storytelling, non-combat spells, and atmospheric worldbuilding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://itch.io/jam/mini-jame-gam-53" rel="noopener noreferrer"&gt;Mini Jame Gam #53&lt;/a&gt;&lt;/strong&gt; (starts 2026-04-03 11:00:00) — A beginner-friendly, fully 2D jam with flexible engine rules, a theme-and-special-object twist, and room for quick prototypes makes this a great chance to build something fun, polished, and creative in a short time.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>news</category>
    </item>
    <item>
      <title>MiniScript Roadmap Update (March 2026)</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Wed, 11 Mar 2026 20:54:08 +0000</pubDate>
      <link>https://forem.com/joestrout/miniscript-roadmap-update-march-2026-41a4</link>
      <guid>https://forem.com/joestrout/miniscript-roadmap-update-march-2026-41a4</guid>
      <description>&lt;p&gt;At the end of 2025, I posted a &lt;a href="https://dev.to/joestrout/miniscript-road-map-for-2026-17mh"&gt;MiniScript Road Map for 2026&lt;/a&gt; (and beyond).  We're now almost one quarter into 2026.  Let's see how it's going!&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.amazonaws.com%2Fuploads%2Farticles%2F2a2cb0oc1ff4x5jzfd5f.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.amazonaws.com%2Fuploads%2Farticles%2F2a2cb0oc1ff4x5jzfd5f.png" alt="MiniScript chinchilla logo" width="128" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MiniScript 2.0
&lt;/h2&gt;

&lt;p&gt;The biggest work planned for this year is MiniScript 2, a complete rewrite of the MiniScript language, focused on performance.  I'm pleased to report that MiniScript 2 is &lt;strong&gt;ahead of schedule&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal for the first quarter was "MS2 compiles &amp;amp; runs a subset of MiniScript", while the Q2 (end of June) goal was "MS2 feature complete (whole language implemented)".  We've pretty much achieved the Q2 goal already!  MS2 has an extensive &lt;a href="https://github.com/JoeStrout/miniscript2/blob/main/tests/testSuite.txt" rel="noopener noreferrer"&gt;test suite&lt;/a&gt; that exercise every part of the language, and longer test programs and benchmarks that put it to work in more realistic ways.  We don't yet have &lt;code&gt;import&lt;/code&gt;, but technically that's not part of the language; that's added by host apps (like &lt;a href="https://miniscript.org/MiniMicro" rel="noopener noreferrer"&gt;Mini Micro&lt;/a&gt;.  We have a short list of &lt;a href="https://github.com/JoeStrout/miniscript2/blob/main/notes/LANGUAGE_CHANGES.md" rel="noopener noreferrer"&gt;new language features&lt;/a&gt; and minor changes, all of which are tested and working except for one (doc strings on functions — we're still thinking about whether and how we want that to work).&lt;/p&gt;

&lt;p&gt;But what about performance, you ask?&lt;/p&gt;

&lt;p&gt;Yesterday I ran three benchmarks -- two using iteration and one using recursion (a particularly challenging benchmark for MiniScript 1.x).  I ran them in MiniScript 1 (C++ version), MiniScript 2 (both C# and C++ version), and for comparison's sake, also ran equivalent code in Python and Lua.  Here's the result, with the Y axis representing speed relative to Python.&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.amazonaws.com%2Fuploads%2Farticles%2Fy8iwh6cu0kfk0o42451u.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.amazonaws.com%2Fuploads%2Farticles%2Fy8iwh6cu0kfk0o42451u.png" alt="Bar chart showing benchmarks by language" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a couple important things to note here.  First, the C++ version of MS2 is significantly faster (like 15X faster) than the C# version.  That's how it &lt;em&gt;should&lt;/em&gt; be, since C# is managed code run by a bytecode interpreter, but in MS1, those times were about the same.  (I didn't include a MS1 C# benchmark but trust me, it's always been roughly the same as the C++ version.)&lt;/p&gt;

&lt;p&gt;But this doesn't mean the C# version is slow.  Compare the blue bars (MS2 C#) to the teeny gray bars (MS1 C++).  Even the C# version of MiniScript 2 is much faster than the C++ version of MS1, by a factor of 10.&lt;/p&gt;

&lt;p&gt;Now compare the green bar (the C++ version of MS2) to Python (orange bar).  We are much faster on 2 of 3 tests, and about the same on the third.  Recursion still costs a little more than we'd like, but it now merely drags us down to "about the same speed as Python," which is pretty fast.&lt;/p&gt;

&lt;p&gt;Of course there's Lua over there (red bars), feeling pretty good about itself.  Yes, Lua is fast, especially on recursion tests.  On the iterative benchmarks, we're faster on one, a bit slower on the other.  But we're not done yet!&lt;/p&gt;

&lt;p&gt;I also ran some tests where, instead of starting with MiniScript source code, I used hand-written assembly code.  This tests how fast the VM can go, separate from how good the compiler is at emitting optimized bytecode.  Here are the results for that:&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.amazonaws.com%2Fuploads%2Farticles%2Fk5tq4em63ftdl5fr8x7p.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.amazonaws.com%2Fuploads%2Farticles%2Fk5tq4em63ftdl5fr8x7p.png" alt="Bar chart showing asm vs. src" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here the bright bars are hand-written assembly, and the darker bars are MiniScript source.  You can see that the hand-written assembly runs faster, by a factor of 2-3.  This reflects the opportunity for improvement we could get just by improving the compiler, entirely separate from the VM.  &lt;em&gt;We've barely begun to do compiler optimizations yet.&lt;/em&gt;  So I think we'll be able to capture much of that potential improvement.&lt;/p&gt;

&lt;p&gt;To summarize: MiniScript 2 is already fast (comparable or better than Python; nearly as fast or faster than Lua).  And I expect it to get faster before 2.0 ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raylib-MiniScript
&lt;/h2&gt;

&lt;p&gt;Another goal for the year was a full set of bindings for MiniScript, and not just for the web, but for desktop (and maybe other) platforms too.  Here I am pleased to report that &lt;strong&gt;we are even &lt;em&gt;more&lt;/em&gt; ahead of schedule&lt;/strong&gt;.  This was a goal for the 4th quarter (end of the year), but, well, some of us got excited and it's nearly done!&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.amazonaws.com%2Fuploads%2Farticles%2F22bhovp9j5qfmguvpuph.gif" 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.amazonaws.com%2Fuploads%2Farticles%2F22bhovp9j5qfmguvpuph.gif" alt="Animated gif of raylib-miniscript demo menu" width="480" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the aid of helpful community members (especially &lt;a href="https://github.com/dcrawl" rel="noopener noreferrer"&gt;Dcrawl&lt;/a&gt;), essentially the entire Raylib library has now been wrapped -- all &lt;a href="https://github.com/JoeStrout/raylib-miniscript/blob/main/API_DOC.md" rel="noopener noreferrer"&gt;736 functions&lt;/a&gt;.  These include support for shaders and even 3D graphics:&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.amazonaws.com%2Fuploads%2Farticles%2Fku9ftmz1br45i4zjl28o.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fku9ftmz1br45i4zjl28o.gif" alt="Animated gif of 3D graphics demo" width="508" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like &lt;a href="https://github.com/JoeStrout/MSRLWeb" rel="noopener noreferrer"&gt;MSRLWeb&lt;/a&gt;, on which it is based, the new &lt;a href="https://github.com/JoeStrout/raylib-miniscript" rel="noopener noreferrer"&gt;raylib-miniscript&lt;/a&gt; library performs shockingly well.  This despite it being currently built on MiniScript 1.  As soon as possible, we'll shift it over to MiniScript 2, for a substantial boost in performance.&lt;/p&gt;

&lt;p&gt;The raylib-miniscript library is currently available only in source form, and has only seen substantial testing on MacOS and Linux.  Nonetheless, several users are not waiting, but are already writing or porting projects to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Soda
&lt;/h2&gt;

&lt;p&gt;I'm among those porting projects to raylib-miniscript; in my case, it's &lt;a href="https://joestrout.itch.io/inversion-institute" rel="noopener noreferrer"&gt;Inversion Institute&lt;/a&gt;, a logic/puzzle game that's been on the back burner since 2023.  To port this Mini Micro game to Raylib, I am writing classes (in MiniScript) that mimic the Mini Micro APIs... in other words, I am (re)writing Soda on the way to updating my game.&lt;/p&gt;

&lt;p&gt;You can find the files for that currently in the Inversion Institute repo, for example &lt;a href="https://github.com/JoeStrout/InversionInstitute/blob/main/rlms/assets/lib/PixelDisplay.ms" rel="noopener noreferrer"&gt;PixelDisplay.ms&lt;/a&gt; and &lt;a href="https://github.com/JoeStrout/InversionInstitute/blob/main/rlms/assets/lib/SpriteDisplay.ms" rel="noopener noreferrer"&gt;SpriteDisplay.ms&lt;/a&gt;.  So, while I originally had no actual intention of starting on the Soda rewrite until MiniScript 2 is done, in fact I'm already writing and testing the code that will end up becoming Soda.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what's next?
&lt;/h2&gt;

&lt;p&gt;The broad 50,000-foot ambitions outlined a couple months ago have largely been reduced to a laundry list of smaller, more specific remaining to-do items:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MiniScript 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Further optimize the MiniScript 2 compiler&lt;/li&gt;
&lt;li&gt;Write &lt;strong&gt;moar tests&lt;/strong&gt; and benchmarks to ensure it works properly&lt;/li&gt;
&lt;li&gt;Modify or wrap the internal APIs so that people embedding MiniScript in their C#/C++ apps can easily update to MS2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Raylib-MiniScript:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;update to a MiniScript 2 backend&lt;/li&gt;
&lt;li&gt;set up build scripts/procedures for Mac/Win/Linux/Web&lt;/li&gt;
&lt;li&gt;publish prebuilt binaries, so users don't have to build it themselves&lt;/li&gt;
&lt;li&gt;improve documentation/examples&lt;/li&gt;
&lt;li&gt;retire MSRLWeb (now superceded by this project)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Soda:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retire existing SDL code, replace with raylib-miniscript foundation&lt;/li&gt;
&lt;li&gt;get API coverage at or beyond Soda's current status (enabling trivial porting of current Soda apps)&lt;/li&gt;
&lt;li&gt;continue to API parity (or beyond) with Mini Micro&lt;/li&gt;
&lt;li&gt;publish prebuilt binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mini Micro 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;continue the &lt;a href="https://github.com/JoeStrout/minimicro2" rel="noopener noreferrer"&gt;rewrite&lt;/a&gt; once MiniScript 2 is done (and Raylib-MiniScript is using it)&lt;/li&gt;
&lt;li&gt;release for current platforms&lt;/li&gt;
&lt;li&gt;release for new platforms (Raspberry Pi, phones, tablets)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Summoner Game Platform:&lt;/strong&gt;&lt;br&gt;
This one is still at the 50,000-foot level.  It will be built on the technologies above, so... it has to wait.  But while previously I didn't expect to even start on this until 2027, now I think we might see it sooner than that.&lt;/p&gt;

&lt;p&gt;So that's where we are.  I'm really quite excited about how far we've come so far in 2026, and what is now on the near horizon.  What about you?  Which of this stuff are you most looking forward to?  Please leave your comments below!&lt;/p&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>programming</category>
      <category>roadmap</category>
    </item>
    <item>
      <title>MiniScript Road Map for 2026</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Tue, 30 Dec 2025 15:02:13 +0000</pubDate>
      <link>https://forem.com/joestrout/miniscript-road-map-for-2026-17mh</link>
      <guid>https://forem.com/joestrout/miniscript-road-map-for-2026-17mh</guid>
      <description>&lt;p&gt;With 2025 coming to a close, it's time to look ahead to 2026!  MiniScript is now eight years old.  Many programming languages really come into their own when they're about 10 years old.  It looks like this will apply to MiniScript too — just wait till you see all the cool developments coming in the next two years!&lt;/p&gt;

&lt;h2&gt;
  
  
  MiniScript 2.0
&lt;/h2&gt;

&lt;p&gt;The big push for next year will be version 2.0 of MiniScript itself.  This is a complete rewrite of the compiler, bytecode, and virtual machine (VM) underlying the language.  While there will be a handful of minor new features, the main focus of this rewrite is &lt;strong&gt;performance&lt;/strong&gt;.  We think we can make it run dozens of times faster, maybe hundreds of times faster on certain benchmarks.&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.amazonaws.com%2Fuploads%2Farticles%2F2a2cb0oc1ff4x5jzfd5f.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.amazonaws.com%2Fuploads%2Farticles%2F2a2cb0oc1ff4x5jzfd5f.png" alt="MiniScript chinchilla logo" width="128" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, the C++ version of MiniScript will get true garbage collection, just like the C# version, and improved error messages too.  The C++ code is going to be the basis of almost everything else in this roadmap, so it will get a lot of love.  Both versions will rely heavily on suites of unit tests and integration tests, to ensure the language is rock solid.&lt;/p&gt;

&lt;p&gt;After a bunch of &lt;a href="https://github.com/JoeStrout/MS2Prototypes/" rel="noopener noreferrer"&gt;prototype work&lt;/a&gt;, development of MiniScript 2 is now underway.  Follow its progress &lt;a href="https://github.com/JoeStrout/miniscript2" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  MiniScript Raylib Bindings
&lt;/h2&gt;

&lt;p&gt;I've recently been exploring &lt;a href="https://www.raylib.com/" rel="noopener noreferrer"&gt;Raylib&lt;/a&gt;, a low-level abstraction layer over OpenGL, audio, files, and game input devices.  And I am really impressed.  Raylib is simple, lean, and easy to use.&lt;/p&gt;

&lt;p&gt;So, with help from Discord users dcrawl, minerobber, and Redspark, I quickly put together &lt;a href="https://github.com/JoeStrout/MSRLWeb" rel="noopener noreferrer"&gt;MSRLWeb&lt;/a&gt;, an environment that lets you use Raylib in MiniScript to make 2D web browser (HTML) games.  You don't even need a compiler — you just write MiniScript code, drop it into a folder with the MSRLWeb binary and any sound/image assets you need, and point a web server at it.  See the &lt;a href="https://joestrout.github.io/MSRLWeb/" rel="noopener noreferrer"&gt;online demo&lt;/a&gt;, as well as &lt;a href="https://joestrout.itch.io/spiritcraft" rel="noopener noreferrer"&gt;this game&lt;/a&gt; that I built in it for a 3-hour game jam.&lt;/p&gt;

&lt;p&gt;Raylib has a huge community, and bindings enabling its use from a wide variety of languages.  MSRLWeb provides MiniScript bindings, and is ideal for making a web games.  But next year, we will polish up a general &lt;strong&gt;MiniScript Raylib&lt;/strong&gt; package, that lets you make desktop or even mobile games this way too.  And we'll start promoting MiniScript as an attractive option to Raylib users.  It's a natural fit: both MiniScript (the language) and Raylib (the API) are clean, minimal, and easy to learn, making them an ideal combo for people who want their dev tools to get out of the way so they can express their creativity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Soda Rewrite
&lt;/h2&gt;

&lt;p&gt;For several years now, we've made (very slow) progress on &lt;a href="https://github.com/JoeStrout/soda" rel="noopener noreferrer"&gt;Soda&lt;/a&gt;, a game engine built on C++ and SDL.  Soda lets you use the MiniScript language with an API very similar to Mini Micro, but unlike Mini Micro, it doesn't simulate a retro computer — games you create with it run windowed or full-screen, and have raw access to the host computer, just like games made in Unity, Unreal, Godot, etc.&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.amazonaws.com%2Fuploads%2Farticles%2Fvodfe4n5tzco6in54ugb.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.amazonaws.com%2Fuploads%2Farticles%2Fvodfe4n5tzco6in54ugb.png" alt="Soda logo" width="128" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But progress on Soda has been slow in large part because SDL, the platform abstraction layer I chose for it, is just painful to work with.  Its API is complex; its build system is quirky (especially if you want the same code to work on all platforms); its documentation is poor to non-existent; and it requires extensions to do things that ought to be built in.  Getting anything done with it is a chore, and so lots of things never got done.&lt;/p&gt;

&lt;p&gt;Once MiniScript 2 is done, we will rewrite Soda using Raylib.  Thanks to our experience writing MSRLWeb, I'm confident that this is going to be a much faster, smoother process.  The result will be what we always wanted for Soda: a complete implementation of the Mini Micro API, and then some, in an unfettered environment usable for game projects of any size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini Micro 2.0
&lt;/h2&gt;

&lt;p&gt;Also after MiniScript 2, and again leveraging Raylib, we are going to develop version 2.0 of Mini Micro.  This will finally remove our dependency on Unity, and enable us to run on platforms we could not practically run on before, like the Raspberry Pi.  We'll aim to finally release Mini Micro for Android and iOS tablets, too.&lt;/p&gt;

&lt;p&gt;Mini Micro is likely to get a handful of new features, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;box-drawing characters and alternate text sizes in the text display&lt;/li&gt;
&lt;li&gt;a sort of system debugger/BIOS screen to aid with development&lt;/li&gt;
&lt;li&gt;better support for non-QWERTY keyboards&lt;/li&gt;
&lt;li&gt;fast vector/matrix math&lt;/li&gt;
&lt;li&gt;better support for widescreen (16:10) displays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But in fact the whole system is likely to feel much faster, thanks to MiniScript 2's inherent performance boost (and the fact that Mini Micro will itself be written in C++ rather than C#).  That, in addition to running on ARM hardware, is the real gain for Mini Micro 2.  Your existing projects should Just Work, only more efficient and on more platforms.  Existing resources (like &lt;a href="https://introtocomputerprogramming.online/#cover" rel="noopener noreferrer"&gt;&lt;em&gt;Introduction to Computer Programming&lt;/em&gt;&lt;/a&gt;) will continue to apply.&lt;/p&gt;

&lt;p&gt;An initial, very early prototype of Mini Micro 2 is already &lt;a href="https://github.com/JoeStrout/minimicro2" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.  Give it a star to let me know you care!&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini Micro Hardware?!
&lt;/h2&gt;

&lt;p&gt;Mini Micro is a neo-retro &lt;em&gt;virtual&lt;/em&gt; home computer.  But what if it could be an &lt;em&gt;actual&lt;/em&gt; home computer?&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.amazonaws.com%2Fuploads%2Farticles%2Fmhplcp11dgc5lz0e9xwe.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.amazonaws.com%2Fuploads%2Farticles%2Fmhplcp11dgc5lz0e9xwe.png" alt="Mock-up of Mini Micro running full-screen on Raspberry Pi 500" width="800" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many users have expressed a wish for just such a machine, something that boots quickly and directly into Mini Micro, so they can hack around and develop their games and demos with no distractions.&lt;/p&gt;

&lt;p&gt;With Mini Micro 2, we think this will be possible.  It won't actually be dedicated hardware; it will probably be something like the &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-500/" rel="noopener noreferrer"&gt;Raspberry Pi 500&lt;/a&gt;, which has the computer built into the keyboard itself, much like the Commodore 64 (though much more sleek and modern).  Or for a laptop, something like &lt;a href="https://www.amazon.com/acer-Aspire-Go-15-AG15-51P-510U/dp/B0F8HHTQGV" rel="noopener noreferrer"&gt;this Acer 15"&lt;/a&gt; with a 1920x1200 display.  In either case, we would develop a custom Linux distribution that strips away all the stuff we don't need, and launches directly into Mini Micro in full-screen mode with crisp, pixel-perfect graphics and stereo sound.&lt;/p&gt;

&lt;p&gt;We'll probably have a couple different hardware options that we test and optimize for, though if you're reasonably tech savvy, you could probably get our custom Linux distro to work on other hardware too.  In any case, it will look and feel very much like a dedicated Mini Micro computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summoner Game Platform
&lt;/h2&gt;

&lt;p&gt;Finally, we've begun to dream of building a whole game platform around MiniScript, to make it easier and safer for both developers and players.  We're calling this the &lt;a href="https://summoner.gp" rel="noopener noreferrer"&gt;&lt;em&gt;Summoner Game Platform&lt;/em&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fi7ug2pp8op3l44nizdz3.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.amazonaws.com%2Fuploads%2Farticles%2Fi7ug2pp8op3l44nizdz3.png" alt=" " width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nobody likes to think about it, but you actually take a pretty big risk every time you download a game to your computer.  How do you know it's not malware?  In addition, there is the hassle of downloading, unzipping or otherwise unpacking, installing it if installation is required, and running it... and then if it's not your cup of tea, tracking down everywhere it installed stuff to remove it.&lt;/p&gt;

&lt;p&gt;Summoner Game Platform will address both issues.  A Summoner game will have a unique ID/URL, which takes you to a web page about it; but paste that same ID/URL into the Summoner app (or click a "summoner://" deep link), and it will seamlessly download or update the game and launch it.  The app will also let you easily see what games you have downloaded, and uninstall them (or if you choose, uninstall the game but keep the game data, in case you change your mind).  And as for safety, Summoner games will be sandboxed: able to access only a restricted and safe part of your file system, unable to log keystrokes when the game is not frontmost, etc.&lt;/p&gt;

&lt;p&gt;Summoner games will be written in MiniScript, using either the Mini Micro/Soda API or the lower-level Raylib bindings.  Further down the road, we might even build support for mods (also written in MiniScript!) right into the Summoner client, making it easy for both mod developers and users.  It'll also support a "tip jar" for each game (and mod?) so you can show your appreciation to your favorite devs.  All this will make Summoner a great platform for everything from quick game-jam games, to large professional-quality projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  All this &lt;em&gt;when?&lt;/em&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Predictions are difficult, especially about the future."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All that goodness above is a lot of work, though once MiniScript 2.0 (MS2) is done, the rest of it has a lot of overlap — it's mostly Raylib, C++, and MiniScript combined in various ways.  My main goal is to finish MS2 in time for the holidays next year.  The other projects will follow rapidly after that.  Something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026 Q1&lt;/td&gt;
&lt;td&gt;MS2 compiles &amp;amp; runs a subset of MiniScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 Q2&lt;/td&gt;
&lt;td&gt;MS2 feature complete (whole language implemented)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 Q3&lt;/td&gt;
&lt;td&gt;MS2 testing, refinement, &amp;amp; polish&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 Q4&lt;/td&gt;
&lt;td&gt;MS2 released 🥳&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 Q4&lt;/td&gt;
&lt;td&gt;Full RayLib bindings for all platforms 👾&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2027 Q1&lt;/td&gt;
&lt;td&gt;Soda 1.0 (full API) 🥤&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2027 Q2&lt;/td&gt;
&lt;td&gt;Mini Micro 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2027 Q3&lt;/td&gt;
&lt;td&gt;Mini Micro hardware/distro 🖥️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2027 Q4&lt;/td&gt;
&lt;td&gt;Summoner Game Platform initial release 🎮&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here are some things you can do to keep apprised of our progress, or even help move it along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow &lt;a href="https://dev.to/joestrout"&gt;me&lt;/a&gt; here on dev.to for future updates.&lt;/li&gt;
&lt;li&gt;Follow &lt;a href="https://bsky.app/profile/miniscript.bsky.social" rel="noopener noreferrer"&gt;MiniScript on Bluesky&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Star and Watch the various GitHub projects linked above, especially &lt;a href="https://github.com/JoeStrout/miniscript2" rel="noopener noreferrer"&gt;MiniScript 2&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Join us &lt;a href="https://discord.gg/7s6zajx" rel="noopener noreferrer"&gt;on Discord&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Participate on the &lt;a href="https://forums.miniscript.org/" rel="noopener noreferrer"&gt;forums&lt;/a&gt; (these have been rather dead lately as everyone has moved to Discord, but we'd love to see more activity there).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sponsors/JoeStrout" rel="noopener noreferrer"&gt;Sponsor me&lt;/a&gt; on GitHub.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what do you think of these plans?  Share your thoughts in the comments below!&lt;/p&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>programming</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Read live data from Google Sheets in Mini Micro</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Wed, 17 Dec 2025 02:11:14 +0000</pubDate>
      <link>https://forem.com/joestrout/read-live-data-from-google-sheets-in-mini-micro-4j3k</link>
      <guid>https://forem.com/joestrout/read-live-data-from-google-sheets-in-mini-micro-4j3k</guid>
      <description>&lt;p&gt;Recently I've started working with a small team on a 5-month game jam.  We're making a 2D rocket-building game for &lt;a href="https://miniscript.org/MiniMicro/" rel="noopener noreferrer"&gt;Mini Micro&lt;/a&gt;.  This involves some data tables defining the tech tree, and the rocket parts unlocked by each tech node.  To make it easy for everyone on the team to view and update that data, we're keeping it (for now) online in Google Sheets — and our Mini Micro code can read that data &lt;em&gt;directly&lt;/em&gt;.  That means that as soon as you update the data in the sheet, you can just re-run the game and it immediately uses the new data.&lt;/p&gt;

&lt;p&gt;This is an amazingly useful trick.  I'll show you how you can do it, too!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Publish the sheet as TSV
&lt;/h2&gt;

&lt;p&gt;The first step is to open the Google Sheets document you want to access, and from the File menu (within the web page), choose &lt;strong&gt;Share ▶︎ Publish to web&lt;/strong&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.amazonaws.com%2Fuploads%2Farticles%2F1vs2zkz04s5f9fd996a1.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.amazonaws.com%2Fuploads%2Farticles%2F1vs2zkz04s5f9fd996a1.png" alt="Screen shot showing Share ▶︎ Publish to web" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will pop up a dialog that lets you set various options; the most important one is the format pop-up, which you should change from "Web page" to "Tab-separated values (.tsv)".&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.amazonaws.com%2Fuploads%2Farticles%2F7cfm69ckyp8prvdgvcaj.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.amazonaws.com%2Fuploads%2Farticles%2F7cfm69ckyp8prvdgvcaj.png" alt="Publish to Web dialog screenshot" width="781" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check over the other options too in case there's anything you want to change.  Then click the Publish button.&lt;/p&gt;

&lt;p&gt;Once you publish a spreadsheet, it's published forever and updated immediately whenever the data changes (you never need to re-publish it).  That's why in my screen shot above, the dialog points out that this document is already published — I did that yesterday.&lt;/p&gt;

&lt;p&gt;Once you publish, copy the URL that it shows you.  (You can always go back and use the &lt;strong&gt;Share ▶︎ Publish to web&lt;/strong&gt; command again to see that URL again if you need it.)&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Read and parse the TSV in Mini Micro
&lt;/h2&gt;

&lt;p&gt;Because we published in Tab-Separated Value (or "TSV") format, we'll get the data as a big string, with rows separated by line breaks (or &lt;code&gt;char(10)&lt;/code&gt; in MiniScript), and columns separated by tabs (&lt;code&gt;char(9)&lt;/code&gt;).  It wouldn't be hard to write some code to split the string into a list of lines, and then split each line into a list of fields... but why bother?  Mini Micro has a built-in TSV module that does all that for you!&lt;/p&gt;

&lt;p&gt;Once you &lt;code&gt;import "tsv"&lt;/code&gt; and get a URL to a published TSV sheet, pulling that data down from the web and parsing it (into a set of maps — assuming the first row is headers and other rows are data) is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data = tsv.parse(http.get(url))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's so easy, you can do it right on the command line!&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.amazonaws.com%2Fuploads%2Farticles%2Fvikj3d2oun5cuifw7be9.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.amazonaws.com%2Fuploads%2Farticles%2Fvikj3d2oun5cuifw7be9.png" alt="Screen shot of Mini Micro accessing Sheets data on the command line" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your spreadsheet has multiple sheets, the base URL will get you the default one (always the leftmost sheet, I believe).  However you can access any sheet you want by going to it in Google Sheets, and noticing the "gid=" part of the URL.&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.amazonaws.com%2Fuploads%2Farticles%2Fdthnwmzgcykywo0ywiho.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.amazonaws.com%2Fuploads%2Farticles%2Fdthnwmzgcykywo0ywiho.png" alt="Screen shot of Location field showing gid" width="800" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See that "gid=1681045610" part at the end of the URL?  That number uniquely identifies which sheet you're looking at.  You can append this string (including "&amp;amp;gid=") to your base URL, to get a complete URL that will fetch that specific sheet.&lt;/p&gt;

&lt;p&gt;My game data currently has two sheets: one for parts, and one for the tech tree.  So my Mini Micro code actually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "tsv"

// Use the URL from the "Publish to Web" feature:
baseURL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vRA2AQx4X9PZyyoU5mMV18MdB-OI50dx-AdShkBqMKqSaa8dFhb3USE5vtUF1JlPBjkTZFouuyF3Quj/pub?output=tsv"
partsListURL = baseURL + "&amp;amp;gid=22599298"
techTreeURL = baseURL + "&amp;amp;gid=1681045610"

// Fetch the data
partsData = tsv.parse(http.get(partsListURL))
print "Read " + partsData.len + " parts, such as: "
pprint partsData[0]

techData = tsv.parse(http.get(techTreeURL))
print "Read " + techData.len + " tech nodes, such as: "
pprint techData[0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I run that, I get this:&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.amazonaws.com%2Fuploads%2Farticles%2F4bt5prkendc6jn8i9ulf.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.amazonaws.com%2Fuploads%2Farticles%2F4bt5prkendc6jn8i9ulf.png" alt="Screen shot of above program, showing output of both part and tech data" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...and yes, I just gave you the real URL to my real game data.  You could run this in your own copy of Mini Micro and see &lt;em&gt;all&lt;/em&gt; the parts and tech nodes.&lt;/p&gt;

&lt;p&gt;Go on, do it.  I dare you.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ...There's no Step 3
&lt;/h2&gt;

&lt;p&gt;And that's it!  It's such a simple technique, but such a phenomenally useful thing to do.  It means that the whole team can iterate quickly on the data, without having to change any files.  You could even have multiple people editing it at once, all in real time, without worrying about git conflicts or other pain.  It's just... frictionless.&lt;/p&gt;

&lt;p&gt;When it comes time to actually &lt;em&gt;publish&lt;/em&gt; your game, you'll probably want to download that data as a file (for each sheet), and bundle it with your game.  Then you'll just replace &lt;code&gt;http.get&lt;/code&gt; with &lt;code&gt;file.load&lt;/code&gt;; the rest of your program stays the same.  This will reduce your load time and ensure your game still works even without the internet (assuming it doesn't need internet access for some other reason).&lt;/p&gt;

&lt;p&gt;But during development, this is about the most powerful use of &lt;code&gt;http.get&lt;/code&gt; I can imagine.  &lt;strong&gt;Try it!&lt;/strong&gt;  You won't be disappointed.&lt;/p&gt;

</description>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>programming</category>
      <category>networking</category>
    </item>
    <item>
      <title>Introduction to Computer Programming: Online Edition!</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Mon, 22 Sep 2025 14:19:06 +0000</pubDate>
      <link>https://forem.com/joestrout/introduction-to-computer-programming-online-edition-369j</link>
      <guid>https://forem.com/joestrout/introduction-to-computer-programming-online-edition-369j</guid>
      <description>&lt;p&gt;A few years ago, I wrote an introductory book about computer programming called &lt;a href="https://www.amazon.com/dp/1736167618" rel="noopener noreferrer"&gt;&lt;em&gt;Introduction to Computer Programming&lt;/em&gt;&lt;/a&gt;.  It's a relatively short book (54 pages), with a large, colorful page format and lavishly illustrated by talented artist Allie Daigle.&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.amazonaws.com%2Fuploads%2Farticles%2F8q3w87xhyp3y2cl01t5y.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.amazonaws.com%2Fuploads%2Farticles%2F8q3w87xhyp3y2cl01t5y.png" alt="Introduction to Computer Programming" width="546" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was inspired by some great BASIC programming books I had as a kid, like &lt;a href="https://usborne.com/us/books/computer-and-coding-books" rel="noopener noreferrer"&gt;these&lt;/a&gt;.  They made programming fun and enjoyable.  I never knew that "coding is hard" feeling so many people get nowadays, because in those days, it wasn't; with fun books like these in hand, it was something that kids could easily do.&lt;/p&gt;

&lt;p&gt;In most environments today, programming really &lt;em&gt;is&lt;/em&gt; harder than it was back then.  But with &lt;a href="https://miniscript.org" rel="noopener noreferrer"&gt;MiniScript&lt;/a&gt; and &lt;a href="https://miniscript.org/MiniMicro/" rel="noopener noreferrer"&gt;Mini Micro&lt;/a&gt;, it's even easier now than it was then, and just as much fun.  And this book provides that same fun, engaging jump-start for Mini Micro that those classic books of yore provided for BASIC home computers of their day.&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.amazonaws.com%2Fuploads%2Farticles%2Frz4lbd9hnxgl0szqow6q.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.amazonaws.com%2Fuploads%2Farticles%2Frz4lbd9hnxgl0szqow6q.png" alt="Many robots" width="508" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what if you don't live in a country where this book is sold?  Or what if the cost is problematic?  Or what if, indeed, English is not the most comfortable language for you or your kids to read?&lt;/p&gt;

&lt;h2&gt;
  
  
  Announcing the (Free!) Online Edition
&lt;/h2&gt;

&lt;p&gt;I'm delighted to officially announce the free &lt;em&gt;online edition&lt;/em&gt; of this book!  It has all the same content as the &lt;a href="https://www.amazon.com/dp/1736167618" rel="noopener noreferrer"&gt;print&lt;/a&gt; and &lt;a href="https://www.amazon.com/Introduction-Computer-Programming-kids-MiniScript-ebook/dp/B09CSX1NMP" rel="noopener noreferrer"&gt;ebook&lt;/a&gt; versions, but it's entirely on the web, free for all.  Read it here:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://introtocomputerprogramming.online" rel="noopener noreferrer"&gt;https://introtocomputerprogramming.online&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The layout is responsive to your browser; on a small screen like a phone it will switch to a single-column layout, while on a tablet or desktop, you'll get a two-column layout similar to the original book.&lt;/p&gt;

&lt;p&gt;And, it's being translated into other languages!  English and German are available now, with more on the way.  Just click the little menu button in the top-left corner, and pick your language.&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.amazonaws.com%2Fuploads%2Farticles%2Fun5r283xpvv74gg9c69i.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.amazonaws.com%2Fuploads%2Farticles%2Fun5r283xpvv74gg9c69i.png" alt="Language menu" width="225" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Book Features
&lt;/h2&gt;

&lt;p&gt;The book is perfect for anyone who is curious about computers and programming.  It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A brief overview of how computers work&lt;/li&gt;
&lt;li&gt;Step-by-step guide to getting started&lt;/li&gt;
&lt;li&gt;Colorful robot characters illustrating key points&lt;/li&gt;
&lt;li&gt;19 fun programming puzzles to check your understanding&lt;/li&gt;
&lt;li&gt;Seven complete mini-games you can type in and play&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've never written a line of code in your life, this will get you started quickly.  And if you are a grizzled veteran with decades of experience, you'll enjoy how this book brings back the feels of your youth!&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.amazonaws.com%2Fuploads%2Farticles%2Fictetb5smz1rzqph3qf7.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.amazonaws.com%2Fuploads%2Farticles%2Fictetb5smz1rzqph3qf7.png" alt="print " width="272" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course the original book is still available &lt;a href="https://www.amazon.com/dp/1736167618" rel="noopener noreferrer"&gt;on Amazon&lt;/a&gt; if you want it!  But now you have no reason to wait.  Head on over to &lt;a href="https://introtocomputerprogramming.online" rel="noopener noreferrer"&gt;IntroToComputerProgramming.online&lt;/a&gt; and get started!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>books</category>
    </item>
    <item>
      <title>UV Mapping in Mini Micro</title>
      <dc:creator>JoeStrout</dc:creator>
      <pubDate>Tue, 05 Aug 2025 22:40:01 +0000</pubDate>
      <link>https://forem.com/joestrout/uv-mapping-in-mini-micro-2eoa</link>
      <guid>https://forem.com/joestrout/uv-mapping-in-mini-micro-2eoa</guid>
      <description>&lt;p&gt;&lt;a href="https://miniscript.org" rel="noopener noreferrer"&gt;Mini Micro&lt;/a&gt; version 1.2.5 came out this week, a fairly minor update that fixed a couple of obscure bugs, but also introduced one big feature: the ability to &lt;strong&gt;change the texture coordinates of a sprite&lt;/strong&gt;.  This feature is sometimes known as "UV mapping."&lt;/p&gt;

&lt;h2&gt;
  
  
  What's UV Mapping?
&lt;/h2&gt;

&lt;p&gt;Just as positions on a screen are usually described as X (horizontal) and Y (vertical), positions on a &lt;em&gt;texture&lt;/em&gt; (any image used to define the colors in a sprite or tile map) are described as U (horizontal) and V (vertical).&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.amazonaws.com%2Fuploads%2Farticles%2Ftp183i3yewpajbyir220.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.amazonaws.com%2Fuploads%2Farticles%2Ftp183i3yewpajbyir220.png" alt="Diagram showing U and V coordinates"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A sprite in Mini Micro has four corners, and each corner of the sprite corresponds to some position in this image.  In the standard (default) UV mapping, each corner of the sprite simply maps to the corresponding corner of the texture.&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.amazonaws.com%2Fuploads%2Farticles%2F67widwlc5inz0kcida1j.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.amazonaws.com%2Fuploads%2Farticles%2F67widwlc5inz0kcida1j.png" alt="Diagram showing standard UV mapping"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sprite corners in Mini Micro are always specified in counter-clockwise order, starting with the bottom-left.  So if the sprite above were called &lt;code&gt;spr&lt;/code&gt; and you checked &lt;code&gt;spr.uvs&lt;/code&gt; (one of the new methods introduced in 1.2.5), you would get &lt;code&gt;[[0, 0], [1, 0], [1, 1], [0, 1]]&lt;/code&gt;.  That's because the bottom-left corner of the sprite uses texture (UV) coordinate [0,0], the bottom-right one has UV coordinate [1,0], and so on.&lt;/p&gt;

&lt;p&gt;But now you can use the &lt;code&gt;setUVs&lt;/code&gt; method to change those default coordinates to anything you like!  Suppose we change the first two UV coordinates to [0, 0.5] and [1, 0.5] — moving them up from the bottom of the texture to the middle.  The resulting sprite would like the this (on the left):&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.amazonaws.com%2Fuploads%2Farticles%2F78owc80t17h35hr7mmbs.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.amazonaws.com%2Fuploads%2Farticles%2F78owc80t17h35hr7mmbs.png" alt="Sprite with UV coordinates  raw `[[0, 0.5], [1, 0.5], [1, 1], [0.1]]` endraw "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sprite is just as big as it was before — we haven't changed its size or position on screen.  But we've changed what part of the texture it's using.  So now we see only the top half of the texture, stretched to fill the sprite.&lt;/p&gt;

&lt;p&gt;You don't have to keep your UV values in the 0-1 range, either.  If you go outside this range, with numbers less than 0 or greater than 1, the texture simply repeats.  It can repeat as many times as you like.  Here's an example with UVs set to &lt;code&gt;[[0, 0.5], [2, 0.5], [2, 1], [0, 1]]&lt;/code&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.amazonaws.com%2Fuploads%2Farticles%2Fh8w3inngn656mlfjuswi.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.amazonaws.com%2Fuploads%2Farticles%2Fh8w3inngn656mlfjuswi.png" alt="Sprite with UV coordinates  raw `[[0, 0.5], [2, 0.5], [2, 1], [0, 1]]` endraw "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  OK, but why?
&lt;/h2&gt;

&lt;p&gt;Why would you do this?  It's true that this is a "power user" feature that many devs won't need, especially in the beginning.  That's why Mini Micro never had it before now!  But there are a few cases where it is really handy.&lt;/p&gt;

&lt;p&gt;Suppose you want a sprite to grow up out of the ground.  Before now, you could only do this by using &lt;code&gt;Image.getImage&lt;/code&gt; to extract a bunch of partial images, which is inefficient, or by somehow layering the sprite behind the ground, which is not always possible.  With &lt;code&gt;setUVs&lt;/code&gt;, it's both easier and more efficient.  Code like this can do the job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clear
spr = new Sprite
spr.image = file.loadImage("/sys/pics/Wumpus.png")
display(4).sprites.push spr

bottom = 200; height = 128
left = 480-64; right = 480+64

for v in range(0, 1, 0.01)
    spr.setUVs [[0,1-v], [1,1-v], [1,1], [0,1]]
    top = bottom + v * height
    spr.setCorners [[left, bottom], [right, bottom],
      [right, top], [left, top]]
    yield
end for
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2F3txr9yy9dsva87qx8c15.gif" 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.amazonaws.com%2Fuploads%2Farticles%2F3txr9yy9dsva87qx8c15.gif" alt="Animated screen shot of Wumpus popping up"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or, suppose you want to take a simple tiling texture (like any of those in /sys/pics/textures), and make a screen-wide bar out of it, repeating the texture many times.  Again, methods to do this before were either inefficient or a pain in the next to write.  Now it's easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clear
spr = new Sprite
spr.image = file.loadImage("/sys/pics/textures/ToonWoodB.png")
display(4).sprites.push spr
yield // causes sprite to get fully set up

bottom = 200; top = bottom + 64
left = 0; right = 960
repeats = 960 / 64
spr.setCorners [[left, bottom], [right, bottom],
  [right, top], [left, top]]
spr.setUVs [[0,0], [repeats,0], [repeats,1], [0,1]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2Fwl0x6csx9fw6b3z5kma4.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.amazonaws.com%2Fuploads%2Farticles%2Fwl0x6csx9fw6b3z5kma4.png" alt="Screen shot of wood strip from above code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This works at any scale.  Take a screen-wide background layer, and make it repeat as many times as needed for your level.  It's still a simple quad sprite to Mini Micro, and so extremely efficient.&lt;/p&gt;

&lt;p&gt;UV mapping can also be an efficient way to make use of sprite sheets — large textures that contain smaller images meant for individual sprites (or animation frames).  Again, you could use these before by calling &lt;code&gt;getImage&lt;/code&gt; to break them up.  But just pointing the UV coordinates at the right place may be both easier and more efficient, especially if you're working with tools (spritesheet editors) already set up to give you UV coordinates.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Long Time Ago, On A Server Far, Far Away...
&lt;/h2&gt;

&lt;p&gt;Let's close with something a bit cooler.  Suppose we use &lt;code&gt;setCorners&lt;/code&gt; on our sprite to stretch it out on bottom, and make it narrow at the top, like the floor of a long hallway.  Then we make a texture by drawing a few paragraphs of text into an offscreen PixelDisplay, with plenty of blank space above the actual text.  And finally, we use &lt;code&gt;setUVs&lt;/code&gt; in a loop to scroll the texture up the sprite.&lt;/p&gt;

&lt;p&gt;(If you don't see an image below, give it a moment to load.)&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.amazonaws.com%2Fuploads%2Farticles%2Fm6qmolebt3q2e3pqzw49.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fm6qmolebt3q2e3pqzw49.gif" alt="Animated movie-style 3D scrolling text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result on an actual Mini Micro looks a lot smoother than the above heavily compressed GIF.  So try it yourself!  Here's the code:&lt;/p&gt;

&lt;p&gt;
  Twist open to see ANewHope.ms
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "styledText"
import "bmfFonts"
import "mathUtil"; lerp2d = @mathUtil.lerp2d
min = @mathUtil.min; max = @mathUtil.max

title = "A NEW HOPE"
crawl = [
"It is a period of coding chaos.",
"Rebel programmers, striking from",
"hidden basements, have won their",
"first victory against the evil",
"Punctuation Empire.", "",
"Evading the dreaded STATUS QUO, a",
"group of freedom fighters led by",
"Joe Codewalker has established a new",
"secret language on the remote",
"internet site of MiniScript.org.", "",
"Pursued by agents of the Established",
"Order, the brave rebels race to",
"restore clarity to the code,",
"overcome the dark forces of verbose",
"syntax, and bring joy to the",
"programmers of the galaxy..."]

bmfFonts.Font.printJustified = function(s, x, y, width=500)
    words = s.split
    w = 0
    for word in words; w += font.width(word); end for
    ew = width - w // ("extra width")
    ewPerWord = floor(ew/(words.len-1))
    for i in words.indexes
        font.print words[i], x, y
        if i == words.len-2 then ewPerWord = ew
        x += font.width(words[i]) + ewPerWord
        ew -= ewPerWord
    end for
end function

clear
colors = ["#FFFFFF", "#AAAAFF", "#FFFFAA", "#FFCCCC"]
gfx.clear
for i in range(1000)
    gfx.setPixel rnd*960, rnd*640, colors[rnd * colors.len]
end for

font = bmfFonts.Font.load("/sys/fonts/minimicro-pro-20.bmf")
gfx = new PixelDisplay
gfx.clear color.clear, 480, 4096

y = 480
font.printCentered "A NEW HOPE", 476/2, y
y -= 48

for i in crawl.indexes
    if i+1 == crawl.len or crawl[i+1] == "" then
        font.print crawl[i], 0, y
    else
        font.printJustified crawl[i], 0, y, 476
    end if
    y -= 24
end for
texture = gfx.getImage(0, 0, gfx.width, gfx.height)
gfx = display(5)

spr = new Sprite
spr.image = texture
display(4).sprites.push spr
yield

spr.setCorners [[480-400, 0], [480+400, 0], [480+20, 600], [480-20, 600]]

scrollTo = function(yOffset)
    v = yOffset / texture.height
    dv = 1 - 480 / texture.height

    spr.setUVs [[0, v], [1, v], [1, v+dv], [0, v+dv]]
end function


while true
    scrollTo 500 - time * 20
    yield
end while
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;br&gt;
 &lt;/p&gt;

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

&lt;p&gt;Now you know what UV mapping is, and how to use it in Mini Micro.  Hopefully the examples above have given you some inspiration.  How might you use this in your own games?  Share your thoughts in the comments below!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>miniscript</category>
      <category>minimicro</category>
      <category>sprites</category>
    </item>
  </channel>
</rss>
