<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: wick3dr0se</title>
    <description>The latest articles on DEV Community by wick3dr0se (@wick3dr0se).</description>
    <link>https://dev.to/wick3dr0se</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%2F549067%2F6b775ba6-5f6e-43b6-894d-0bb8243eee4d.jpg</url>
      <title>DEV Community: wick3dr0se</title>
      <link>https://dev.to/wick3dr0se</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wick3dr0se"/>
    <language>en</language>
    <item>
      <title>Writing a Matrix Digital Rain</title>
      <dc:creator>wick3dr0se</dc:creator>
      <pubDate>Tue, 31 Dec 2024 20:22:08 +0000</pubDate>
      <link>https://dev.to/wick3dr0se/writing-a-matrix-digital-rain-296b</link>
      <guid>https://dev.to/wick3dr0se/writing-a-matrix-digital-rain-296b</guid>
      <description>&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%2Frk6oypwew2xacnd01ajz.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%2Frk6oypwew2xacnd01ajz.gif" alt="Matrix Digital Rain" width="458" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Writing a terminal based matrix digital rain is fairly simple; Even utilizing raw ANSI escape sequences, we can make one in under 20 lines of code with a shell scripting language like Bash. Similarly in most programming languages&lt;/p&gt;

&lt;p&gt;For simplicity of the article, we won't implement starting position, random initial character highlighting or anything too fancy. This implementation utilizes the &lt;a href="https://vt100.net/docs/vt510-rm/SGR.html" rel="noopener noreferrer"&gt;Select Graphic Rendition&lt;/a&gt;, specifically the 4-bit green color sequence for portability as well&lt;/p&gt;

&lt;p&gt;For a full-featured implementation, check out the &lt;a href="https://github.com/wick3dr0se/matrix" rel="noopener noreferrer"&gt;matrix digital rain I wrote in Bash&lt;/a&gt; (the inspiration for this post!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bash?
&lt;/h2&gt;

&lt;p&gt;Bash is an excellent choice for this, because Bash comes pre-installed nearly everywhere. Most of the time it's the default shell interpreter. When writing pure Bash, it's super portable and easy to write. Generally Bash performs pretty well if usage of shell invoking procedures (such as command substituion) and external calls are limited&lt;/p&gt;

&lt;h2&gt;
  
  
  Concept
&lt;/h2&gt;

&lt;p&gt;This is just a general, base concept for a minimal matrix digital rain&lt;/p&gt;

&lt;p&gt;My mind works well off concepts - In hopes it could help you, I'll leave this here to give some context on the implementation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trap exit - cleanup
Initialize terminal
Loop forever
    {
        Calculate random column
        Calculate random speed
        Calculate random symbol length
        Loop - over terminal lines
            Calculate random symbol
            Print down calculated column
            If iteration &amp;gt; symbol length, erase
            Sleep (print speed)
    } Send to background
    Sleep (print frequency)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You don't have to use raw ANSI escape sequences, you can use an abstraction like &lt;code&gt;tput&lt;/code&gt; or a &lt;a href="https://github.com/wick3dr0se/bashin" rel="noopener noreferrer"&gt;TUI library I wrote&lt;/a&gt; for writing scripts exactly like this one, among others of course. But for performance and portability's sake, it benefits to utilize ANSI escapes&lt;/p&gt;

&lt;p&gt;Let's start by initializing the terminal. How this works is up to you but I would utilize the terminal's alternative buffer and disable the cursor. Using the alternative buffer has the advantage of disabling scrolling and allowing us to reset the terminal to its initial state&lt;/p&gt;

&lt;p&gt;These sequences are &lt;a href="https://vt100.net/docs/vt100-ug/" rel="noopener noreferrer"&gt;VT100 ANSI escape sequences&lt;/a&gt;, which are the standard, that terminal manipulation libraries abstract away&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;init_term&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# \e is an escape sequence&lt;/span&gt;
    &lt;span class="c"&gt;# the following sequences switch to the alternative buffer and hide the cursor&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e[?1049h\e[?25l'&lt;/span&gt;

    &lt;span class="c"&gt;# sets terminal x and y to COLUMNS/LINES respectively&lt;/span&gt;
    &lt;span class="nb"&gt;shopt&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; checkwinsize&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="p"&gt;;&lt;/span&gt;:&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where it gets a little interesting! We have to do some basic equations. Here we utilize Bash's builtin &lt;code&gt;$RANDOM&lt;/code&gt; variable to give us a random integer in the specified range. First we should calculate our symbols starting column, then our symbols printing delay and finally the amount of symbols to print down a column. We should loop over the terminal lines plus symbol length and calculate a random symbol&lt;/p&gt;

&lt;p&gt;Now it's time to put all our random calculations to work! Simply print the random symbol to the calculated column down the iterated row (line) and follow up behind the symbol length with an erase sequence. Wrapping up our rain function, sleep with the calculated delay to give the rain varying speed. Almost there! This is the bulk of it, and the entire complexity of the implementation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rain&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# random column between 1-terminal columns&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;symbolCol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RANDOM%COLUMNS+1&lt;span class="o"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# random sleep delay between 1-9&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;symbolSpeed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RANDOM%9+1&lt;span class="o"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# random length between 2-10&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;symbolLen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RANDOM%9+2&lt;span class="o"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# walk the terminal lines + symbol length&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; i &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; i &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; LINES+symbolLen&lt;span class="p"&gt;;&lt;/span&gt; i++ &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="c"&gt;# random symbol&lt;/span&gt;
        &lt;span class="nv"&gt;symbol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYMBOLS&lt;/span&gt;:RANDOM%&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;SYMBOLS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:1&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="c"&gt;# print green symbol down calculated column&lt;/span&gt;
        &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e[%d;%dH\e[32m%s\e[m'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$symbolCol&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$symbol&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="c"&gt;# erase after symbol length &lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt; i &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; symbolLen &lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e[%d;%dH\e[m '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;i-symbolLen&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$symbolCol&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="c"&gt;# rain (printing) speed&lt;/span&gt;
        &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="s2"&gt;"0.&lt;/span&gt;&lt;span class="nv"&gt;$symbolSpeed&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After defining our functions, the script is super simple, only requiring a run forever loop and call to our rain function, sent to the background, with a short sleep delay to prevent our rain from absolute downpour and potential CPU havoc. Modifying the terminal state is completely optional but worth it&lt;/p&gt;

&lt;p&gt;Defining some symbols as a string&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SYMBOLS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'0123456789!@#$%^&amp;amp;*()-_=+[]{}|;:,.&amp;lt;&amp;gt;?'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we should probably trap some terminal cleanup so that we can reset our terminal state. Although one could just opt to close the terminal, skipping deinitializing and trapping interrupts entirely. Arguably the next step should be done first but in TUI scripts, it's common to define signal handlers before calling functions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'printf "\e[?1049l\e[?25h"'&lt;/span&gt; SIGEXIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we initialize the terminal by calling our init_term function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;init_term
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To finish it off, we execute the run forever loop, call our rain function in the background and sleep between calls for density&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# loop forever&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# call it and send it to the background&lt;/span&gt;
    rain &amp;amp;

    &lt;span class="c"&gt;# rain density&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;0.1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all!! Here is the entire implementation of our matrix digital rain in pure Bash using raw ANSI escape sequences, in under 20 lines of code!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;init_term&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e[?1049h\e[?25l'&lt;/span&gt;
    &lt;span class="nb"&gt;shopt&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; checkwinsize&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="p"&gt;;&lt;/span&gt;:&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

rain&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;symbolCol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RANDOM%COLUMNS+1&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;symbolSpeed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RANDOM%9+1&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;symbolLen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RANDOM%9+2&lt;span class="o"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; i &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; i &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; LINES+symbolLen&lt;span class="p"&gt;;&lt;/span&gt; i++ &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;symbol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYMBOLS&lt;/span&gt;:RANDOM%&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;SYMBOLS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:1&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e[%d;%dH\e[32m%s\e[m'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$symbolCol&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$symbol&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 
        &lt;span class="o"&gt;((&lt;/span&gt; i &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; symbolLen &lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\e[%d;%dH\e[m '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;i-symbolLen&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$symbolCol&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="s2"&gt;"0.&lt;/span&gt;&lt;span class="nv"&gt;$symbolSpeed&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'printf "\e[?1049l\e[?25h"'&lt;/span&gt; &lt;span class="nb"&gt;exit

&lt;/span&gt;&lt;span class="nv"&gt;SYMBOLS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'0123456789!@#$%^&amp;amp;*()-_=+[]{}|;:,.&amp;lt;&amp;gt;?'&lt;/span&gt;

init_term

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; rain &amp;amp; &lt;span class="nb"&gt;sleep &lt;/span&gt;0.1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope the post was thorough and able to teach you something! If not, well sorry, I tried! Anyway, you're more than welcome to &lt;a href="https://github.com/wick3dr0se/matrix" rel="noopener noreferrer"&gt;support the project&lt;/a&gt; by dropping a star on GitHub!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The original post can be found on my personal website, here: &lt;a href="https://wick3dr0se.github.io" rel="noopener noreferrer"&gt;https://wick3dr0se.github.io&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
