<?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: Marcel Losso Forte</title>
    <description>The latest articles on DEV Community by Marcel Losso Forte (@marcellosso).</description>
    <link>https://dev.to/marcellosso</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3491249%2F60974ab1-16c6-454a-b365-0aa6f88cd952.jpeg</url>
      <title>DEV Community: Marcel Losso Forte</title>
      <link>https://dev.to/marcellosso</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcellosso"/>
    <language>en</language>
    <item>
      <title>I Gave a Monkey 10 Million Keystrokes a Day on My GitHub Profile (It's Trying to Write Hamlet)</title>
      <dc:creator>Marcel Losso Forte</dc:creator>
      <pubDate>Tue, 27 Oct 2026 01:24:30 +0000</pubDate>
      <link>https://dev.to/marcellosso/i-gave-a-monkey-10-million-keystrokes-a-day-on-my-github-profile-its-trying-to-write-hamlet-18mj</link>
      <guid>https://dev.to/marcellosso/i-gave-a-monkey-10-million-keystrokes-a-day-on-my-github-profile-its-trying-to-write-hamlet-18mj</guid>
      <description>&lt;p&gt;Give a monkey a typewriter and infinite time, and it will eventually type the works of Shakespeare. That's the &lt;a href="https://en.wikipedia.org/wiki/Infinite_monkey_theorem" rel="noopener noreferrer"&gt;infinite monkey theorem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I got bored of a normal GitHub profile README. So I put the theorem on mine.&lt;/p&gt;

&lt;p&gt;Every day a GitHub Action hands my pixel monkey 10,000,000 random keystrokes and checks whether any of them landed on Hamlet. The stats update on the README. The longest match so far is "o the bla" — nine characters, somewhere in Act IV. At this pace, finishing the play takes about 10^237421 years. Cool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CLI the monkey pretends to run&lt;/strong&gt;&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;$ &lt;/span&gt;monkey &lt;span class="nt"&gt;--type&lt;/span&gt; &lt;span class="nt"&gt;--keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10M
&lt;span class="c"&gt;# or, when someone feeds a banana:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;monkey &lt;span class="nt"&gt;--type&lt;/span&gt; &lt;span class="nt"&gt;--banana&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@you
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood it's just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node scripts/monkey.mjs &lt;span class="nb"&gt;type&lt;/span&gt;    &lt;span class="c"&gt;# daily: 10M random keystrokes&lt;/span&gt;
node scripts/monkey.mjs feed    &lt;span class="c"&gt;# banana issue: +1M keys + reply&lt;/span&gt;
node scripts/monkey.mjs render  &lt;span class="c"&gt;# redraw SVGs + README from state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How the typing works&lt;/strong&gt;&lt;br&gt;
27 keys (a–z + space), uniform, via crypto.randomBytes. Reject bytes ≥ 243 so every key stays equally likely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ALPHABET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcdefghijklmnopqrstuvwxyz &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DAILY_KEYSTROKES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;65536&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;243&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 243 = 9 * 27&lt;/span&gt;
      &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&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="nx"&gt;ALPHABET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// then find the longest substring that also appears in Hamlet…&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hamlet is normalized (lowercase letters, runs of junk → one space). Matches are looked up in length-keyed Sets, then mapped back to Act / Scene / line for the SVG quote.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feed the monkey&lt;/strong&gt;&lt;br&gt;
Anyone can open a banana issue. Each one buys 1,000,000 extra keystrokes right away. &lt;/p&gt;

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

&lt;p&gt;The Action replies with the snippet and your avatar lands on the donor wall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;🐒🍌 &lt;span class="ge"&gt;*nom.*&lt;/span&gt; Thanks @you! That banana bought 1,000,000 keystrokes. Somewhere in the mess:
to hell allegiance vows to the bla…
                    ^^^^^^^^^
&lt;span class="gs"&gt;**"o the bla"**&lt;/span&gt; (9 characters) appears in Hamlet, Act IV, Scene I:
&lt;span class="gt"&gt;&amp;gt; To hell, allegiance! Vows, to the blackest devil!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No signup. GitHub Issues as a game controller.&lt;/p&gt;

&lt;p&gt;The README is generated, not edited&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generated by scripts/monkey.mjs. Edit the script, not this file.&lt;/span&gt;
&lt;span class="nf"&gt;writeFileSync&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="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;README.md`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;readme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Banners, stats, feed button and project cards are SVGs (light + dark), fonts embedded as base64 so every viewer sees the same type. Full source: &lt;a href="https://github.com/marcellosso/marcellosso/blob/main/scripts/monkey.mjs" rel="noopener noreferrer"&gt;scripts/monkey.mjs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why bother&lt;/strong&gt;&lt;br&gt;
Profile READMEs are usually a skill grid and a wavy “Hi I'm…”. This one is an experiment you can poke. The cards under the monkey point at things I ship — &lt;a href="https://www.penciled.org/" rel="noopener noreferrer"&gt;penciled.js&lt;/a&gt;, &lt;a href="https://www.getlocalhost.app/" rel="noopener noreferrer"&gt;localhost&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/better-auth-localization" rel="noopener noreferrer"&gt;better-auth-localization&lt;/a&gt; — but this post is about the monkey, not a launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;br&gt;
Profile: &lt;a href="https://github.com/marcellosso" rel="noopener noreferrer"&gt;github.com/marcellosso&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Drop a banana. Ruin (or slightly improve) the monkey's day.&lt;/p&gt;

&lt;p&gt;If the monkey ever finishes Hamlet, I'll buy everyone a coffee. Don't hold your breath.&lt;/p&gt;

</description>
      <category>github</category>
      <category>javascript</category>
      <category>fun</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
