<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>schollz</title>
    <link>/</link>
    <description>Recent content on schollz</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 03 Jan 2024 08:00:46 -0700</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml" />
    <item>
	    <title>nyblcore</title>
	    <image>https://schollz.com/img/nyblcore_instructions.png</image>
      <link>/wares/nyblcore/</link>
      <pubDate>Sun, 26 Feb 2023 08:00:46 -0700</pubDate>
      
      <guid>/wares/nyblcore/</guid>
      <description><![CDATA[<style>
table { width: 100%; text-align:center; table-layout:fixed; }
td { 
overflow: hidden; 
text-overflow: ellipsis; 
word-wrap: break-word;
}
h2 {
    padding-top:2em;
}
</style>
<div id="mc_embed_shell" style="border-style: solid; border-color: #777; padding:1em; margin:1em; padding-top:0em; background: #fff; border-radius:1em;box-shadow: 12px 12px 2px 1px rgba(0, 0, 255, .2); margin-top:2em; margin-bottom:2em;">
<div id="mc_embed_signup">
    <form action="https://schollz.us21.list-manage.com/subscribe/post?u=6f305d82cde1a96e67a3c0577&amp;id=8f5c3ddc01&amp;f_id=0009ace1f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_self" novalidate="">
        <div id="mc_embed_signup_scroll"><h3>Sign up to receive an email about <span style="color:rgba(0, 0, 255, 0.6);weight:900;">nyblcore</span> news:</h3>
            <div class="mc-field-group"><label for="mce-EMAIL" style="font-size:1em;font-weight:900;">Email Address: </label><input type="email" name="EMAIL" class="required email" id="mce-EMAIL" required="" value="" style="padding:0.5em; font-size:1em; font-family: 'Roboto Mono', monospace;  margin-right:1em;"><input type="submit" name="subscribe" id="mc-embedded-subscribe" class="button" value="Subscribe" style="font-family: 'Roboto Mono', monospace;padding:0.6em;"></div><div class="mc-field-group" style="display:none;"><label for="mce-ZEPTOCORE">WARES </label><input type="text" name="ZEPTOCORE" class=" text" id="mce-ZEPTOCORE" value="nyblcore"></div>
        <div id="mce-responses" class="clear">
            <div class="response" id="mce-error-response" style="display: none;"></div>
            <div class="response" id="mce-success-response" style="display: none;"></div>
        </div><div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_6f305d82cde1a96e67a3c0577_8f5c3ddc01" tabindex="-1" value="" ></div>
    </div>
</form>
</div>
</div>
<p>nyblcore is&hellip;</p>
<ul>
<li>a tiny <strong>ATtiny85-based lo-fi sample player</strong> device for diy enthusiasts.</li>
<li>powered by a single AAA for <strong>~5-8 hours of battery life</strong>.</li>
<li>holding 8 kB of storage for up to <strong>1.2 seconds of 8-bit audio sampled @ 4.2 kHz</strong>.</li>
<li>controlled with three multi-functional knobs that modulate tempo, volume, distortion, and fx.</li>
<li>manipulated with <strong>three fx knobs</strong> for audio warping (jump, retrig, stutter and stretch).</li>
<li>able to <strong>load custom firmware / audio</strong> using an <a href="https://www.amazon.com/Arduino-A000066-ARDUINO-UNO-R3/dp/B008GRTSV6/?tag=scholl-20&amp;th=1" target="_blank" >Arduino</a> or an <a href="https://www.amazon.com/whiteeeen-Tiny-AVR-Programmer/dp/B09921SC7Z/?tag=scholl-20&amp;th=1" target="_blank" >AVR usb programmer</a>.</li>
<li><a href="#open-source" >open-source*</a>, wonderfully hackable.</li>
</ul>
<h2 id="demo">demo</h2>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/cA8lai6gwKw" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h2 id="buy">buy</h2>
<p>unfortunately, we are currently sold out. devices will be available again on <strong>November 1st</strong>.</p>
<p>please sign up for the mailing list, and you will be notified as soon as devices are ready again:</p>
<!-- Begin Mailchimp Signup Form -->
<form id="newsletter" action="https://schollz.us21.list-manage.com/subscribe/post?u=6f305d82cde1a96e67a3c0577&amp;id=8f5c3ddc01&amp;f_id=0009ace1f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate style="padding-top: -0.5em;">
    <label for="mce-EMAIL">Enter email to get updates about nyblcore:<br>
    </label>
    <input type="email" value="" name="EMAIL" class="required email mchimp" id="mce-EMAIL" style=" width:30%;">
    <input type="text" name="b_6f305d82cde1a96e67a3c0577_8f5c3ddc01" tabindex="-1" value="" style="display: none;" class="mchimp"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button mchimp" style="cursor: pointer; width:15%;">
</form>
<!--End mc_embed_signup-->
<br>
<br>
<center>
<div id='product-component-1681950988687'></div>
<script src="/data/nyblcore.js"></script>
</center>
<br><br>
<p>Questions? Reach out at <a href="mailto:zack@infinitedigits.co" >zack@infinitedigits.co</a> or review the <a href="/wares/policy" >purchase policy</a>.</p>
<h2 id="examples">examples</h2>
<h3 id="example-1-drum-hits">Example 1: drum hits!</h3>
<p><strong>original audio:</strong></p>
<p><audio src="/img/beat0.mp3" class="waveform"></audio></p>
<p><strong>nyblcore:</strong></p>
<p><audio src="/img/beat1.mp3" class="waveform"></audio></p>
<h3 id="example-2-breakbeat">Example 2: breakbeat!</h3>
<p><strong>original audio:</strong></p>
<p><audio src="/img/drum0.mp3" class="waveform"></audio></p>
<p><strong>nyblcore:</strong></p>
<p><audio src="/img/drum1.mp3" class="waveform"></audio></p>
<h3 id="example-3-arp">Example 3: arp</h3>
<p><strong>original audio:</strong></p>
<p><audio src="/img/arp0.mp3" class="waveform"></audio></p>
<p><strong>nyblcore:</strong></p>
<p><audio src="/img/arp1.mp3" class="waveform"></audio></p>
<h3 id="example-4-just-a-sine-wave">Example 4: just a sine wave</h3>
<p><strong>original audio:</strong></p>
<p><audio src="/img/sine0.mp3" class="waveform"></audio></p>
<p><strong>nyblcore:</strong></p>
<p><audio src="/img/sine1.mp3" class="waveform"></audio></p>
<h2 id="documentation">documentation</h2>
<p>full instructions are available here: <a href="https://nyblcore.com/guide" target="_blank" >download pdf</a></p>
<h3 id="soldering-tutorial">soldering tutorial</h3>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/mrZleTfIKjE" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h2 id="open-source">open-source</h2>
<p>nyblcore is open-source. in addition, all the code and boards are on <a href="https://github.com/schollz/nyblcore" target="_blank" >my github</a>. If you use the public repo to build a nyblcore, I ask that you consider <a href="https://github.com/sponsors/schollz" target="_blank" >sponsoring me on Github</a> to help me continue making devices and software that is open-source.</p>
<h2 id="limitations--possibilities">limitations = possibilities</h2>
<p>The tiny size of nyblcore is both its strength and its limitation. The limited memory and storage space only allow for a small amount of audio and effects. Additionally, the tempos may not be entirely stable and there is no external sync, but this can also be seen as an opportunity to embrace the unpredictability and randomness of the device.</p>
<p>I hope that nyblcore may be a welcome challenge or a way to break out of creative ruts. or maybe it can serve as a source of inspiration for creating new sounds and effects.</p>
<h2 id="upload">upload</h2>
<p>You can <a href="/data/nyblcore_base.ino" >download the original firmware here</a>. Or use this form to generate a new firmware with your audio for the nyblcore:</p>
<style>
    textarea {
        width: 100%;
        background: #fff;
        padding: 0.5em;
    }
    .output {
        margin: 1rem 0;
    }
    html {
        font-family: sans-serif;
    }
    #myForm {
        background: #f1f1f1;
        padding: 20px;
        border: 1px solid black;
    }
    #myForm ol {
        padding-left: 0;
    }
    #myForm li {
        display: flex;
        justify-content: space-between;
        margin-bottom: 10px;
        list-style-type: none;
        border: 1px solid black;
    }
    #myForm img {
        height: 64px;
        order: 1;
    }
    #myForm p {
        line-height: 32px;
        padding-left: 10px;
    }
    #myForm label,
    #myForm button,
    #myForm input {
        background-color: #7f9ccb;
        padding: 5px 10px;
        border-radius: 5px;
        border: 1px ridge black;
        font-size: 0.8rem;
        height: auto;
    }
    #myForm label:hover,
    #myForm button:hover {
        background-color: #2d5ba3;
        color: white;
    }
    #myForm label:active,
    #myForm button:active {
        background-color: #0d3f8f;
        color: white;
    }
    .error {
        color: #ff0000;
    }
    #spinner {
        display: inline-block;
        width: 50px;
        height: 50px;
        border: 3px solid rgba(255, 255, 255, 0.3);
        border-radius: 50%;
        border-top-color: #fff;
        animation: spin 1s ease-in-out infinite;
        -webkit-animation: spin 1s ease-in-out infinite;
    }
    @keyframes spin {
        to {
            -webkit-transform: rotate(360deg);
        }
    }
    @-webkit-keyframes spin {
        to {
            -webkit-transform: rotate(360deg);
        }
    }
</style>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.3.0/jquery.form.min.js" integrity="sha384-qlmct0AOBiA2VPZkMY3+2WqkHtIQ9lSdAsAn5RUJD/3vA5MKDgSGcdmIv4ycVxyn" crossorigin="anonymous"></script>
<script>
    $(function () {
        $("#myForm").ajaxForm({
            target: "#renderedHTML",
            beforeSubmit: function () {
                $("#spinner").fadeIn("slow");
                $([document.documentElement, document.body]).animate({ scrollTop: $("#spinner").offset().top }, 2000);
            },
            success: function () {
                $("#spinner").fadeOut("fast");
                $("#renderedHTML").fadeIn("slow");
            },
        });
    });
</script>
<form id="myForm" method="post" action="https://api.nyblcore.com/" enctype="multipart/form-data" >
    <div><label for="image_uploads" style="font-size: 1em; cursor: pointer;">Choose audio</label> <input type="file" id="image_uploads" name="files" accept=".wav, .aif, .ogg, .flac" multiple  /></div>
    <div id="previewFiles" style="background-color: #fff;"><p>no files selected</p></div>
    <div style="padding-top: 0.25em;">Seconds (max 1.2 seconds): <br><input type="decimal" name="seconds" min="0" max="1.2" value="1.2" style="font-size: 1em;" /><br /></div>
    <div>Slices (single files only): <input type="number" name="slices" min="1" max="100" value="5" style="font-size: 1em;" /><br /></div>
    <div style="padding-top: 0.25em;">Crossfade (ms): <br><input type="number" name="crossfade" min="0" max="1000" value="0" style="font-size: 1em;" /><br /></div>
    <div style="padding-top: 1em;"><button style="font-size: 1em; cursor: pointer;">Submit</button></div>
</form>
<center>
    <span id="spinner" style="display: none;">
        <br />
        <br />
        spinner!<br />
        <br />
    </span>
</center>
<span id="renderedHTML">
</span>
<br>
<br>
<br>
<script>
    const input = document.getElementById("image_uploads");
    const previewFiles = document.getElementById("previewFiles");
    input.style.opacity = 0;
    input.addEventListener("change", updateImageDisplay);
    function updateImageDisplay() {
        while (previewFiles.firstChild) {
            previewFiles.removeChild(previewFiles.firstChild);
        }
        const curFiles = input.files;
        if (curFiles.length === 0) {
            const para = document.createElement("p");
            para.textContent = "No files currently selected for upload";
            previewFiles.appendChild(para);
        } else {
            const list = document.createElement("ol");
            previewFiles.appendChild(list);
            for (const file of curFiles) {
                const listItem = document.createElement("li");
                const para = document.createElement("p");
                if (validFileType(file)) {
                    para.textContent = `${file.name}, file size ${returnFileSize(file.size)}.`;
                    listItem.appendChild(para);
                } else {
                    para.textContent = `File name ${file.name}: Not a valid file type. Update your selection.`;
                    listItem.appendChild(para);
                }
                list.appendChild(listItem);
            }
        }
    }
    const fileTypes = ["audio/wav", "audio/flac", "audio/ogg", "audio/mpeg","audio/x-wav", "audio/x-flac", "audio/x-ogg", "audio/x-mpeg"];
    function validFileType(file) {
      console.log(file.type);
        return fileTypes.includes(file.type);
    }
    function returnFileSize(number) {
        if (number < 1024) {
            return number + "bytes";
        } else if (number > 1024 && number < 1048576) {
            return (number / 1024).toFixed(1) + "KB";
        } else if (number > 1048576) {
            return (number / 1048576).toFixed(1) + "MB";
        }
    }
    function copy() {
        let textarea = document.getElementById("textarea");
        textarea.select();
        document.execCommand("copy");
    }
</script>
<h2 id="bytebeat">bytebeat</h2>
<p>this is an alternative firmware that is compatible with the nyblcore. download <a href="https://raw.githubusercontent.com/schollz/nyblcore/main/bytebeat/bytebeat.ino" target="_blank" >the bytebeat firmware from github</a> and upload it as you would normally (see instructions below).</p>
<p>in the bytebeat firmware the selector knob (middle) selects between two modes:</p>
<ol>
<li>selector fully counter-clockwise: in this mode the leftmost knob (knob a) will change volume/distortion. the rightmost knob (knob b) will select a bytebeat formula from ~40 different formulas.</li>
<li>selector knob fully clockwise: in this mode the leftmost knob will select a parameter in the current bytebeat formula. the rightmost knob will change that parameter in the current bytebeat formula. changes are reset upon power-cycling.</li>
</ol>
<h2 id="uploading-firmware">Uploading firmware</h2>
<p>If you are starting from scratch, skip this for now and  <a href="#uploading-firmware-tools" >read further below</a> about the tools you need for the tools necessary to upload the firmware.</p>
<p>It is easy to upload a firmware obtained from the <a href="#upload" >upload form</a>. Simply copy the resulting code:</p>
<p><img src="https://user-images.githubusercontent.com/6550035/233196926-6b05b1a4-4ea0-4eb0-8786-a3b743f84ae5.png" alt="Uploaded audio"></p>
<p>You can copy and paste this directly into the Arduino IDE. Then take out the ATtiny85 from the nyblcore board and put it in your programmer and plug your programmer into your computer. Now just hit the upload button on Arduino to upload the new firmware and voila!</p>
<p><img src="https://user-images.githubusercontent.com/6550035/233197400-03ea760a-eabb-4eca-8b45-8541af3ff5dc.png" alt="Arduino IDE"></p>
<p>You can take out the programmer and put the newly loaded ATtiny85 back into the nyblcore board to enjoy the new sounds.</p>
<h2 id="uploading-firmware-tools">Uploading firmware tools</h2>
<p>Changing the audio on the ATtiny85 requires uploading new firmware. Uploading new firmware is easy but it requires  <a href="https://www.amazon.com/Programmer-Tools-ATTiny85-ATTiny-Arduino/dp/B0BK9P76BH/" target="_blank" >a USB ATtiny85 programmer</a>. (You can also use an Arduino, see the <a href="https://www.instructables.com/How-to-Program-an-Attiny85-From-an-Arduino-Uno/" target="_blank" >instructions for programming</a>, though make sure to use 16 MHz not 8 when selecting the speed.).</p>
<p><img src="/img/sparkfun_programmer.webp" alt="programmer"></p>
<p>Windows users: you will also need <a href="https://github.com/adafruit/Adafruit_Windows_Drivers/releases/tag/2.5.0.0" target="_blank" >drivers for the USB ATtiny85 programmer</a>.</p>
<p>To start, make sure you <a href="https://www.arduino.cc/en/software" target="_blank" >install the Arduino IDE</a>. Open the software and goto <code>File -&gt; Preferences</code> and where it says <code>Additional Boards Manager URLs</code> you can click a button to add an additional line. Add the following line:</p>
<pre tabindex="0"><code>https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json
</code></pre><p>Now restart the Arduino IDE. Now you can goto <code>Tools -&gt; Board: -&gt; ATtiny Microcontrollers</code> and select <code>ATtiny25/45/85</code>.</p>
<p><img src="https://user-images.githubusercontent.com/6550035/233195515-e8d1367b-203c-4c53-bcbc-61b3ae34015a.png" alt="Attiny selection"></p>
<p>Before you begin you will need one of the following pieces of hardware. You can either purchase a USB ATtiny85 programmer (like <a href="https://www.amazon.com/whiteeeen-Tiny-AVR-Programmer/dp/B09921SC7Z/?tag=scholl-20&amp;th=1" target="_blank" >this one</a>) OR</p>
<p>To upload new firmware, first make sure that you <a href="https://www.arduino.cc/en/software" target="_blank" >install the Arduino IDE</a>. Any version will work.</p>
<p>Next, you will need to select <code>Tools -&gt; Clock -&gt; Internal 16 Mhz</code>.</p>
<p><img src="https://user-images.githubusercontent.com/6550035/233195852-3319e177-6096-41b0-a7ab-ef0f34e5fdba.png" alt="Clock setting"></p>
<p>Finally, you will need to select <code>Tools -&gt; Programmer -&gt; USBtinyISP</code>.</p>
<p><img src="https://user-images.githubusercontent.com/6550035/233196149-7f6d4c16-921b-4cee-9243-9a2a6342318a.png" alt="USBtinyISP"></p>
<p>That&rsquo;s it! If you run into trouble feel free to send me a message at <a href="mailto:zack@infinitedigits.co" >zack@infinitedigits.co</a> .</p>
<p>Happy audio warping!</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>pikocore</title>
	    <image>https://schollz.com/img/pikocore_instructions.png</image>
      <link>/wares/pikocore/</link>
      <pubDate>Sun, 02 Apr 2023 08:00:46 -0700</pubDate>
      
      <guid>/wares/pikocore/</guid>
      <description><![CDATA[<style>
table { width: 100%; text-align:center; table-layout:fixed; }
td { 
overflow: hidden; 
text-overflow: ellipsis; 
word-wrap: break-word;
}
</style>
<div id="mc_embed_shell" style="border-style: solid; border-color: #777; padding:1em; margin:1em; padding-top:0em; background: #fff; border-radius:1em;box-shadow: 12px 12px 2px 1px rgba(0, 0, 255, .2); margin-top:2em; margin-bottom:2em;">
<div id="mc_embed_signup">
    <form action="https://schollz.us21.list-manage.com/subscribe/post?u=6f305d82cde1a96e67a3c0577&amp;id=8f5c3ddc01&amp;f_id=0009ace1f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_self" novalidate="">
        <div id="mc_embed_signup_scroll"><h3>Sign up to receive an email about <span style="color:rgba(0, 0, 255, 0.6);weight:900;">pikocore</span> news:</h3>
            <div class="mc-field-group"><label for="mce-EMAIL" style="font-size:1em;font-weight:900;">Email Address: </label><input type="email" name="EMAIL" class="required email" id="mce-EMAIL" required="" value="" style="padding:0.5em; font-size:1em; font-family: 'Roboto Mono', monospace;  margin-right:1em;"><input type="submit" name="subscribe" id="mc-embedded-subscribe" class="button" value="Subscribe" style="font-family: 'Roboto Mono', monospace;padding:0.6em;"></div><div class="mc-field-group" style="display:none;"><label for="mce-ZEPTOCORE">WARES </label><input type="text" name="ZEPTOCORE" class=" text" id="mce-ZEPTOCORE" value="pikocore"></div>
        <div id="mce-responses" class="clear">
            <div class="response" id="mce-error-response" style="display: none;"></div>
            <div class="response" id="mce-success-response" style="display: none;"></div>
        </div><div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_6f305d82cde1a96e67a3c0577_8f5c3ddc01" tabindex="-1" value="" ></div>
    </div>
</form>
</div>
</div>
<p>pikocore is&hellip;</p>
<ul>
<li>a <strong>lo-fi music mangler based on the Raspberry Pi Pico</strong> (brother of the <a href="/wares/nyblcore" >nyblcore</a>).</li>
<li>capable of holding <strong>8 minutes of 8-bit 33 kHz monophonic samples</strong>.</li>
<li>powered by <strong>a single AAA battery for up to 3 hours or by powered by USB-C</strong>.</li>
<li><strong>tempo-synced</strong> with a selectable BPM between 60 and 300, with samples mangled by <strong>beat-synced effects</strong> (stutter, retrig, gate, tunneling).</li>
<li>loaded with <strong>real-time effect</strong>s like a resonant filter, timestretching, volume, and wavefolding.</li>
<li>sequenced with a <strong>128-step sequencer</strong> with recording/playback</li>
<li>saved and loaded via EEPROM for <strong>instant patch recall</strong>.</li>
<li>able to <strong>load custom firmware, new samples</strong>, all through USB-C.</li>
<li><strong>sync-compatible</strong> with Pocket Operators.</li>
<li><a href="https://pikocore.com/source" target="_blank" >open-source</a>, wonderfully hackable.</li>
</ul>
<p>for more info, check out <a href="https://www.studiobrootle.com/pikocore/" target="_blank" >my interview with Studio Brootle</a> and a <a href="https://muziquemagazine.com/the-pikocore-from-infinitedigits-unleashing-creativity-in-a-tiny-package/" target="_blank" >review by DJ Iceman</a>.</p>
<h2 id="demo">demo</h2>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/cB3kc5KxCLs" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<p><strong>original audio:</strong></p>
<p><audio src="/img/demo0.mp3" class="waveform"></audio></p>
<p><strong>pikocore:</strong></p>
<p><audio src="/img/demo1.mp3" class="waveform"></audio></p>
<h2 id="buy">buy</h2>
<center>
<div id='product-component-1688648208080'></div>
<script src="/data/pikocore.js"></script>
</center>
<br>
<br> 
<p>in addition to my store, you can buy assembled devices from Perfect Circuit: <a href="https://www.perfectcircuit.com/infinitedigits-pikocore.html" target="_blank" >click here to go to Perfect Circuit</a>. also, if you are in the UK, diy kits are available from Thonk:  <a href="https://www.thonk.co.uk/shop/infinite-digits-pikocore/" target="_blank" >click here to go to Thonk</a>.</p>
<p>sign up for the mailing list to get notified about updates and new things.</p>
<!-- Begin Mailchimp Signup Form -->
<form id="newsletter" action="https://schollz.us21.list-manage.com/subscribe/post?u=6f305d82cde1a96e67a3c0577&amp;id=8f5c3ddc01&amp;f_id=0009ace1f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate style="padding-top: -0.5em;">
    <label for="mce-EMAIL">Enter email to get pikocore updates:<br>
    </label>
    <!-- <label for="mce-EMAIL">Enter email to get a message when pikocore is available (July 2023):<br>
    </label> -->
    <input type="email" value="" name="EMAIL" class="required email mchimp" id="mce-EMAIL" style=" width:30%;">
    <input type="text" name="b_6f305d82cde1a96e67a3c0577_8f5c3ddc01" tabindex="-1" value="" style="display: none;" class="mchimp"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button mchimp" style="cursor: pointer; width:15%;">
</form>
<!--End mc_embed_signup-->
<br>
<br>
<h2 id="cases">cases</h2>
<p>Pikocore cases are now available for the DIY pikocore, sold separately through <a href="https://dichstudios.com/products/pikocore-case-diy-version" target="_blank" >Dich Studios</a>.  Features: Frosted buttons which allow the LEDs to shine through, Full access to the battery, usb and audio ports. A matching power switch cap for easy operation, Rubber feet to minimize sliding, and several slots for attaching a lanyard.</p>
<p><img src="https://dichstudios.com/cdn/shop/files/1000000506_1024x1024.jpg?v=1706630800" alt="Pikocore case"></p>
<h2 id="usage">usage</h2>
<p>full instructions are available here: <a href="https://pikocore.com/guide" target="_blank" >download pdf</a></p>
<h3 id="bom">bom</h3>
<p>there is an alternative build guide developed from <a href="http://noise.kitchen/" target="_blank" >noise.kitchen</a> available here: <a href="/pdf/Pikocore_build_guide.pdf" >download pdf</a></p>
<details><summary>Click here to view schematic.</summary>
<p><img src="/img/pikocore_schematic.png" alt="pikocore schematic"></p>
</details>
<details><summary>Click here to view the full BOM.</summary>
<table>
<thead>
<tr>
<th>item</th>
<th>quantity</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.aliexpress.us/item/3256803909832318.html" target="_blank" >Pico w/ RGB</a></td>
<td>1</td>
</tr>
<tr>
<td><a href="https://www.aliexpress.us/item/3256803189509513.html" target="_blank" >5V DC-DC step-up</a></td>
<td>1</td>
</tr>
<tr>
<td><a href="https://www.aliexpress.us/item/3256801898453742.html" target="_blank" >1.8mm LED</a></td>
<td>8</td>
</tr>
<tr>
<td><a href="https://www.aliexpress.us/item/3256805418260300.html" target="_blank" >Audio jack (PJ-320B straight)</a></td>
<td>3</td>
</tr>
<tr>
<td><a href="https://www.amazon.com/gp/product/B07XF3YMJ4" target="_blank" >6x6x4.5 button</a></td>
<td>8</td>
</tr>
<tr>
<td><a href="https://www.aliexpress.us/item/3256802927118424.html" target="_blank" >AAA Battery holder (45 mm between pins)</a></td>
<td>1</td>
</tr>
<tr>
<td><a href="https://www.amazon.com/gp/product/B08H58MZNL/" target="_blank" >SPDT switch</a></td>
<td>1</td>
</tr>
<tr>
<td>1x3 male header</td>
<td>1</td>
</tr>
<tr>
<td>10k potentiometer (RV09, 12.5 mm)</td>
<td>4</td>
</tr>
<tr>
<td>1x20 female header</td>
<td>2</td>
</tr>
<tr>
<td>1x20 male header</td>
<td>2</td>
</tr>
<tr>
<td>0.1 uF capacitor</td>
<td>2</td>
</tr>
<tr>
<td>1 uF capacitor</td>
<td>1</td>
</tr>
<tr>
<td>10 kohm resistor</td>
<td>3</td>
</tr>
<tr>
<td>1 kohm ressitor</td>
<td>9</td>
</tr>
<tr>
<td>100 ohm resistor</td>
<td>3</td>
</tr>
<tr>
<td>2N3904 NPN transistor</td>
<td>1</td>
</tr>
</tbody>
</table>
</details>
<br>
<h3 id="tutorial">tutorial</h3>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/mKPq1Chm9Tg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h3 id="basics">basics</h3>
<p>there are eight buttons and four knobs that can be used to manipulate the pikocore. there are two outputs and one input. make sure to plug in only stereo cables to any outputs or inputs.</p>
<h3 id="io">i/o</h3>
<p>the top left jack is for incoming trigger signal (e.g. from a pocket operator in SYN1 mode or a pikocore). the top right jack sends out audio. the bottom right jack sends out trigger signal and audio signal (for syncing to pocket operators using SYN4 mode).</p>
<h3 id="buttons">buttons</h3>
<p>The buttons on the device can be used to jump to samples and retrigger audio in real time.</p>
<p><strong>jumping</strong>: Press any of the eight buttons to jump to that relative position in the sample.</p>
<p><strong>retriggers:</strong> Press and hold a button and press another button to create a retrigger effect. The retrigger speed is based on the position of the second button press. Random effects add added to the retriggers, such as changes in speed, pitch, filter envelopes, or volume envelopes.</p>
<p><strong>stop/play:</strong> press the two leftmost buttons and the two rightmost buttons simultaneously to stop/play.</p>
<p>Button mashing is encouraged.</p>
<h3 id="knobs">knobs</h3>
<p>The rightmost knob is a filter/volume knob that controls the output level. The leftmost knob is the <strong>selector knob</strong>, which can choose between eight different positions. Each position changes a different pair of parameters, which can be changed using the middle two knobs, <strong>knob A</strong> and <strong>knob B</strong>.</p>
<table>
<thead>
<tr>
<th><strong>selector pos</strong></th>
<th><strong>knob a</strong></th>
<th><strong>knob b</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>sample</td>
<td>break</td>
</tr>
<tr>
<td>2</td>
<td>filter</td>
<td>stretch</td>
</tr>
<tr>
<td>3</td>
<td>gate</td>
<td>gate prob.</td>
</tr>
<tr>
<td>4</td>
<td>jump prob.</td>
<td>retrig prob.</td>
</tr>
<tr>
<td>5</td>
<td>tunnel prob.</td>
<td>reverse prob.</td>
</tr>
<tr>
<td>6</td>
<td>sequencer rec.</td>
<td>sequencer on</td>
</tr>
<tr>
<td>7</td>
<td>save</td>
<td>load</td>
</tr>
<tr>
<td>8</td>
<td>volume/fold</td>
<td>tempo</td>
</tr>
</tbody>
</table>
<ul>
<li><strong>sample</strong> changes the sample being played (holds &gt;100 samples totaling ~8 minutes).</li>
<li><strong>break</strong> modifies all probabilities simultaneously using varied easing functions.</li>
<li><strong>filter</strong> is a resonant low-pass filter that attenuates higher frequencies.</li>
<li><strong>stretch</strong> performs a lofi timestretch effect.</li>
<li><strong>gate</strong> controls the amount of gating on the sample.</li>
<li><strong>gate prob.</strong> controls the probability of gating.</li>
<li><strong>jump prob.</strong> controls the likelihood of jumping to a different step in the current sample.</li>
<li><strong>retrig prob.</strong> controls the likehood of retriggering.</li>
<li><strong>tunnel prob.</strong> controls the likelihood of jumping to a different sample.</li>
<li><strong>reverse prob.</strong> controls the probability of reversing direction.</li>
<li><strong>sequencer rec</strong> will record sequences, up to 128 steps (cw = record, ccw = erase).</li>
<li><strong>sequencer on</strong> turns on the sequencer (cw = on, ccw = off).</li>
<li><strong>save</strong> saves probabilities, sample, volume, and tempo (cw = save).</li>
<li><strong>load</strong> recalls the last save (cw = load).</li>
<li><strong>volume/fold</strong> changes the volume and adds a wavefolding effect.</li>
<li><strong>tempo</strong> controls the tempo in steps of 5 bpm (50-305), <a href="#tempo" >encoded in binary</a>.</li>
</ul>
<h2 id="syncing">syncing</h2>
<p>pikocore uses a audio click signal to sync and is fully compatible with sync signals coming from other devices, like the teenage engineering pocket operators. The pikocore syncing operates at 2 PPQN and has max input/output voltages of 3V.</p>
<p>The upper left audio jack takes in a PO sync signal (<code>SYN1</code> on Pocket Operator) and will automatically sync the pikocore. The output sync is separate from the main audio output jack, situated below the Pico mcu. When using a pocket operator, set the receiving device to <code>SYN4</code> (or <code>SYN5</code> if the chain continues.</p>
<h2 id="clock-locking-available-in-version-v12">clock locking (available in version v1.2+)</h2>
<p>you can &ldquo;lock&rdquo; the clock so that the beat continually stays on track and doesn&rsquo;t get diverted when you jump between samples. this is super helpful if you are syncing and you don&rsquo;t want to get &ldquo;off&rdquo; beat when jumping between audio.</p>
<p>you can activate/deactivate this feature by pressing the middle four buttons (yellow = on, teal = off).</p>
<p>more information in this video:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/mGCbNr5GLCU" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h2 id="tempo">tempo</h2>
<p>Set the tempo by turning the <strong>selector knob</strong> fully CW to the eighth spot and then turning <strong>knob b</strong>. The BPM available is between 50 and 305, and the current value is displayed using binary encoding. Instead of memorizing the binary encoding, you can use tool below to visualize the tempo you want to match. Change the slider and then on the pikocore move <strong>knob b</strong> so that the lights match up to get this bpm. Because of the clock in the pikocore, BPMs are only accurate to 2%.</p>
<div style=text-align:center;margin:1em>
<input type=range min=50 max=305 value=120 class=slider id=sliderRange step=5><input type=number id=sliderNum value=120 min=50 max=305 step=5><br>
<canvas id=bpmCanvas></canvas>
</div>
<br>
<script src=https://cdnjs.cloudflare.com/ajax/libs/rough.js/2.1.1/rough.min.js integrity="sha512-lTLYEWPPtelBz0gl9pujLocRWfHSKaULk5s8vrpzjBqPmmCTByfkhRRwBX8Vvq6hY8IKxATwI4q1qsqb9WtY1g==" crossorigin=anonymous referrerpolicy=no-referrer></script>
<script>function redrawCanvas(e){const t=document.getElementById("bpmCanvas");t.getContext("2d").clearRect(0,0,t.width,t.height);const i=rough.canvas(document.getElementById("bpmCanvas"),{seed:18});i.rectangle(5,5,t.width-10,t.height-10,{seed:18,strokeWidth:1});for(let h=7;h>=0;h--){let n=0;e>0&&(n=e%2,e=Math.floor(e/2));const d=h<4?0:1,l=h<4?h:h-4;i.rectangle(t.width/13+l*t.width/4,t.height/5+d*t.height/2,t.width/10,t.width/10,{fill:"#777",fillWeight:1}),i.circle(t.width/8+l*t.width/4,t.height/10*3+d*t.height/2,t.width/20,{fill:"#111",hachureAngle:60,fillWeight:3}),n>0?i.rectangle(t.width/16+l*t.width/4,t.height/12+d*t.height/2,t.width/8,10,{fill:"#EE4B2B",fillWeight:3}):i.rectangle(t.width/16+l*t.width/4,t.height/12+d*t.height/2,t.width/8,10,{})}}document.getElementById("sliderRange").oninput=function(){document.getElementById("sliderNum").value=this.value,redrawCanvas(this.value-50)},document.getElementById("sliderNum").oninput=function(){document.getElementById("sliderRange").value=this.value,redrawCanvas(this.value-50)},redrawCanvas(70)</script>
<h2 id="midi">midi</h2>
<p>the pikocore is capable of listening to midi using a <a href="/wares/ittybittymidi" >itty bitty midi</a> device plugged into its clock input.</p>
<p><img src="/img/ittybittymidi_connection.jpg" alt="itty bitty midi connections"></p>
<p>this behavior needs to be enabled at the firmware level, and the <a href="#customized-firmware" >firmware tool below</a> can be used to update the settings to listen to midi.</p>
<p>the pikocore automatically sets tempo and syncs to a midi clock. it also listens to midi start/stop/continue commmands to synchronize with sequencers. the firmware tool lets you change the midi clock division. you can also toggle a setting in the firmware to have the key presses of a midi keyboard work as buttons, so you can sequence your own button presses.</p>
<h2 id="update">update</h2>
<p>Updating the pikocore is easy. Simply press the &ldquo;boot&rdquo; button and plug in a USB-C cable, or press and hold &ldquo;boot&rdquo; while pressing &ldquo;rst&rdquo; when the USB-C cable is already plugged into your computer.</p>
<p><img src="/img/firmware.gif" alt="Example of hitting buttons"></p>
<p>Then download the <code>.uf2</code> file (either from factory reset firmware, or customized firmware, see below) and drag-and-drop it to the drive that pops up in your computer (usually named <code>RPI-RP2</code>). Visual instructions are available with <a href="https://www.youtube.com/watch?v=VG0q74ASlLQ&amp;t=140s" target="_blank" >this YouTube video that explains the process for uploading firmware</a>.</p>
<h2 id="factory-reset">factory reset</h2>
<h3 id="firmware">firmware</h3>
<p>download these to get the pikocore back to the &ldquo;stock&rdquo; image or upgrade it to a newer image.</p>
<!-- - Jan 12th, 2024: v1.2.1 (small fix) [pikocore-926aa9-midi.uf2 100 (midi enabled)](/data/pikocore-926aa9-midi.uf2), [pikocore-76a1b5-clock.uf2 (clock enabled)](/data/pikocore-76a1b5-clock.uf2) -->
<ul>
<li>Feb 2, 2024: v1.2.2 (fix saving) <a href="/data/pikocore-g1fb3baa-midi.uf2" >.uf2 w/ midi enabled</a>, <a href="/data/pikocore-g1fb3baa-clock.uf2" >.uf2 w/ clock enabled</a></li>
<li>Dec 2nd, 2023: <a href="/data/pikocore-73bf07d.uf2.zip" >pikocore-73bf07d.uf2 (v1.1.0, midi enabled)</a></li>
<li>Sept 6th, 2023: <a href="/data/pikocore-ad319a8.uf2.zip" >pikocore-ad319a8.uf2 (v1.0.1, clock enabled, no RGB led)</a></li>
<li>July 4th, 2023: <a href="/data/pikocore-1ca6156.uf2.zip" >pikocore-1ca6156.uf2 (v1.0.1, clock enabled)</a></li>
</ul>
<h3 id="customized-firmware">customized firmware</h3>
<p>Use this tool to change the audio on the pikocore. This will automatically utilize the latest firmware of the pikocore.</p>
<p>Uploaded audio is automatically converted to the base sample rate (33 kHz) and to the base BPM (165 BPM). If you know the BPM of your sample, you can help the server detect it by including it in the filename somewhere as <code>bpmX</code>. For example, <code>my_drum_bpm150.wav</code> will automatically be registered as 150 BPM. You can also include the number of beats in the filename somewhere as <code>beatsY</code>. For example, <code>my_drum_bpm150_beats4.wav</code> will be registered as having 4 beats. If no information is included, the server will do its best to detect the BPM and then determine the number of beats to be used for beat syncing.</p>
<style>textarea{width:100%;background:#fff;padding:.5em}.output{margin:1rem 0}html{font-family:sans-serif}#myForm{background:#f1f1f1;margin:0 auto;padding:20px;border:1px solid #000}#myForm ol{padding-left:0}#myForm li{display:flex;justify-content:space-between;margin-bottom:10px;list-style-type:none;border:1px solid #000}#myForm img{height:64px;order:1}#myForm p{line-height:32px;padding-left:10px}#myForm button,#myForm input,#myForm label{background-color:#7f9ccb;padding:5px 10px;border-radius:5px;border:1px ridge #000;font-size:.8rem;height:auto}#myForm button:hover,#myForm label:hover,.pikolink:hover{background-color:#2d5ba3;color:#fff}#myForm button:active,#myForm label:active{background-color:#0d3f8f;color:#fff}.error{color:red}#spinner{display:inline-block;width:50px;height:50px;border:3px solid rgba(255,255,255,.3);border-radius:50%;border-top-color:#fff;animation:spin 1s ease-in-out infinite;-webkit-animation:spin 1s ease-in-out infinite}@keyframes spin{to{-webkit-transform:rotate(360deg)}}@-webkit-keyframes spin{to{-webkit-transform:rotate(360deg)}}.pikolink{background:#7f9ccb;padding:.5em;border-radius:1em;text-decoration:none;color:#000}</style><script crossorigin=anonymous integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" src=https://code.jquery.com/jquery-3.5.1.min.js></script><script crossorigin=anonymous integrity=sha384-qlmct0AOBiA2VPZkMY3+2WqkHtIQ9lSdAsAn5RUJD/3vA5MKDgSGcdmIv4ycVxyn src=https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.3.0/jquery.form.min.js></script><script>$((function(){$("#myForm").ajaxForm({target:"#renderedHTML",beforeSubmit:function(){$("#spinner").fadeIn("slow"),$([document.documentElement,document.body]).animate({scrollTop:$("#spinner").offset().top},2e3)},success:function(){$("#spinner").fadeOut("fast"),$("#renderedHTML").fadeIn("slow")}})}))</script><form action="https://api.pikocore.com/" enctype=multipart/form-data id=myForm method=post style=max-width:500px><span style="float:right;">v1.2.1</span><div><label for=image_uploads style=font-size:1em;cursor:pointer>Choose audio file(s) to upload</label><div style=padding-top:.5em><small>(no more than 254 files and/or 8 minutes)</small></div><input accept=".wav, .aif, .ogg, .flac" id=image_uploads multiple name=files style=float:right;position:absolute type=file></div><div style=background-color:#fff id=previewFiles><p>No files currently selected for upload</p></div><div>Select pico version:<select name=size16mb><option value=16>16 mb (black)</option><option value=4>4 mb</option><option value=2>2 mb (green)</option></select></div><div style="padding-top:.5em">Enable RGB led?:<select name="enablergb"><option value="yes">yes</option><option value="no">no</option></select></div><div style="padding-top:.5em">Input as clock or midi?:<select name="clockormidi"><option value="clock" selected="selected">clock</option><option value="midi">midi</option></select></div><div id="midimultiplier" style="padding-top:.5em;display:none">Midi clock multiplier?:<select name="midimultiplier"><option value="1">1/2</option><option value="2" selected="selected">1</option><option value="4">2</option></select></div><div id="midiresetevery" style="padding-top:.5em;display:none">Midi reset every:<select name="midiresetevery"><option value="1">1 beat</option><option value="2">2 beats</option><option value="4">4 beats</option><option value="8">8 beats</option><option value="16" selected="selected">16 beats</option><option value="24">24 beats</option><option value="32">32 beats</option><option value="48">48 beats</option><option value="64">64 beats</option></select></div><div id="miditrack" style="padding-top:.5em;display:none">Midi key on jumping:<select name="miditrack"><option value="1">on</option><option value="0" selected="selected">off</option></select></div><script>function showMidi(){"midi"==document.querySelector('select[name="clockormidi"]').value?(document.getElementById("midimultiplier").style.display="block",document.getElementById("midiresetevery").style.display="block",document.getElementById("miditrack").style.display="block"):(document.getElementById("midimultiplier").style.display="none",document.getElementById("midiresetevery").style.display="none",document.getElementById("miditrack").style.display="none")}document.querySelector('select[name="clockormidi"]').addEventListener("change",function(e){showMidi()}),showMidi()</script><div style=padding-top:1em><button style=font-size:1em;cursor:pointer>Upload</button></div></form><center><span id=spinner style=display:none><br><br>spinner!<br><br></span></center><span id=renderedHTML></span><script>const input=document.getElementById("image_uploads"),previewFiles=document.getElementById("previewFiles");function updateImageDisplay(){for(;previewFiles.firstChild;)previewFiles.removeChild(previewFiles.firstChild);const e=input.files;if(0===e.length){const e=document.createElement("p");e.textContent="No files currently selected for upload",previewFiles.appendChild(e)}else{const t=document.createElement("ol");previewFiles.appendChild(t);for(const i of e){const e=document.createElement("li"),n=document.createElement("p");validFileType(i)?(n.textContent=`${i.name}, file size ${returnFileSize(i.size)}.`,e.appendChild(n)):(n.textContent=`File name ${i.name}: Not a valid file type. Update your selection.`,e.appendChild(n)),t.appendChild(e)}}}input.style.opacity=0,input.addEventListener("change",updateImageDisplay);const fileTypes=["audio/wav","audio/flac","audio/ogg","audio/mpeg","audio/x-wav","audio/x-flac","audio/x-ogg","audio/x-mpeg"];function validFileType(e){return console.log(e.type),fileTypes.includes(e.type)}function returnFileSize(e){return e<1024?e+"bytes":e>1024&&e<1048576?(e/1024).toFixed(1)+"KB":e>1048576?(e/1048576).toFixed(1)+"MB":void 0}function copy(){document.getElementById("textarea").select(),document.execCommand("copy")}</script>
<h3 id="alternative-firmware">alternative firmware</h3>
<p>there are some great alternative firmwares that utilize the same hardware but with a different solution!</p>
<h4 id="pikobeats">pikobeats</h4>
<p>a drum sequencer for the pikocore.</p>
<p>by @<a href="https://github.com/rheslip" target="_blank" >rheslip</a>! source code: <a href="https://github.com/rheslip/PikoBeats" target="_blank" >https://github.com/rheslip/PikoBeats</a></p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/46V_G48qLg4" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h4 id="piko-33">piko-33</h4>
<p>a sample sequencer for the Pikocore.</p>
<p>also by @<a href="https://github.com/rheslip" target="_blank" >rheslip</a>! source code: <a href="https://github.com/rheslip/Piko-33" target="_blank" >https://github.com/rheslip/Piko-33</a></p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/13mzp-h5AR8" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h2 id="remixing">remixing</h2>
<p><img src="/img/pikocore_watercolor.png" alt="watercolor of pikocore"></p>
<p>(water color image by <a href="https://www.youtube.com/watch?v=jPrxLE8y4Us" target="_blank" >Sebtic Smile</a>)</p>
<p>send me your remixes! they light up my day (literally!). there are some amazing ingenuity with creating enclosures for pikocore:</p>
<ul>
<li><a href="https://www.instagram.com/p/CwnkhzlNwWp/" target="_blank" >arcade box!</a> and <a href="https://www.instagram.com/p/CvAm6gLtVA8/" target="_blank" >button box</a> by 2ononen</li>
<li><a href="https://www.instagram.com/p/CvLGuW4rahf/" target="_blank" >arcade box</a> by ground_grown_circuits</li>
<li><a href="https://www.instagram.com/reel/Cvfd3Bkt9is/?utm_source=ig_embed&amp;utm_campaign=loading" target="_blank" >printable case</a> w/ 3D step files available (<a href="https://www.printables.com/en/model/542960-pikocore-case" target="_blank" >regular case</a>, <a href="https://www.printables.com/en/model/575858-pikocore-expanded" target="_blank" >expanded case</a>)</li>
</ul>
<h2 id="troubleshooting">troubleshooting</h2>
<p>Here are some fixes for issues that may crop up when using pikocore.</p>
<p><strong>The audio is really quiet.</strong> This happens if you plug in a mono cable to the audio output. Make sure to use only 3.5mm stereo cables and make sure they are pluggined in all the way.</p>
<p><strong>There is a high pitched sound.</strong> This seems to happen sometimes while powered by the AAA battery and the potentiometers are in a certain position. Try turning the potentiometers CCW to eliminate the noise. If that doesn&rsquo;t work, try rotating the battery in is holder. If it is still a problem, try powering from a USB powerbank.</p>
<p><strong>There is no sound, or the sound is muffled sounding.</strong> This can happen if the filter gets set fully closed, or the timestretch is at 100%, or the volume if fully down. The best thing to do is to reset these three parameters. Move the <strong>selector knob</strong> to the second position and turn <strong>knob a</strong> fully CW and turn <strong>knob b</strong> fully CCW. This will reset the filter and the timestretch.  Now move the <strong>selector knob</strong> to the eight and last position and try to turn <strong>knob a</strong> until you can hear something (the volume). This should fix it 90% of the time.</p>
<p><strong>If you still have issues:</strong> Please feel free to send me an email at <a href="mailto:zack@infinitedigits.co" >zack@infinitedigits.co</a>.</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>itty bitty midi</title>
	    <image>https://schollz.com/img/ittybittymidi.png</image>
      <link>/wares/ittybittymidi/</link>
      <pubDate>Sat, 25 Nov 2023 08:00:46 -0700</pubDate>
      
      <guid>/wares/ittybittymidi/</guid>
      <description><![CDATA[<style>
table { width: 100%; text-align:center; table-layout:fixed; }
td { 
overflow: hidden; 
text-overflow: ellipsis; 
word-wrap: break-word;
}
</style>
<p><em>itty bitty midi</em> a simple circuit that takes either a MIDI din input or a MIDI trs type-a input, and converts it to a logic-level serial bit stream. the resulting bit stream can be used by *core devices (like the <a href="/wares/pikocore" >pikocore</a>) to listen to MIDI signals directly through the clock inputs.</p>
<p>there are two versions of this product, select the one that suits you: <strong>the active version</strong> utilizes a battery-powered circuit with the 6n138 optocoupler to effectively isolate the downstream and upstream devices. <strong>the passive version</strong> does not use isolation and has no battery.</p>
<p>the <em>itty bitty midi</em> is <a href="#source" >open-source</a>.</p>
<h2 id="buy">buy</h2>
<center>
<div id='product-component-1701297837663'></div>
<script src="/data/ittybittymidi.js"></script>
</center>
<br>
<br> 
<h2 id="demo">demo</h2>
<p>here is a little demo of me synchronizing the output of the tb03 with the pikocore using the midi out from the tb03 into the itty bitty midi, into the clock input of the pikocore.</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/S21kX3wV4wU" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<h2 id="source">source</h2>
<p>it is easy and encouraged to DIY the <em>itty bitty midi</em>. both the passive and active versions are easy to make with throughhole components, but the passive one is by far the easiest if you have a few capactiors and a DIN plug.</p>
<details><summary>active version schematic</summary>
<p><img src="/img/Schematic_ittybittymidi-active-v2_2023-11-29.png" alt="itty bitty midi active schematic"></p>
</details>
<details><summary>passive version schematic</summary>
<p><img src="/img/Schematic_ittybittymidi-smd-v3_2023-11-29.png" alt="itty bitty midi active schematic"></p>
</details>
<h2 id="getting-started">getting started</h2>
<p>the usage of <em>itty bitty midi</em> is simple enough: connect your midi device &ldquo;midi out&rdquo; with midi din cable (5-pin) or a midi trs type a cable to the itty bitty midi.</p>
<p>then connect a stereo cable from the output of the <em>itty bitty midi</em> (the headphone jack facing away from the 5-pin din jack) to the clock input of a pikocore or zeptocore.</p>
<p><img src="/img/ittybittymidi_connection.jpg" alt="itty bitty midi connections"></p>
<h2 id="usage">usage</h2>
<p>once connected, the device will listen to incoming midi. in the case of the <a href="/wares/pikocore" >pikocore</a>, it will listen to midi start/stop/continue commands to start &amp; stop on beat. the pikocore also listens to the midi timing commands and automically synchronizes. for more information, see <a href="/wares/pikocore#midi" >the pikocore midi help</a>.</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>go and zig</title>
	    <image>https://schollz.com/img/zig-go.png</image>
      <link>/tinker/go-and-zig/</link>
      <pubDate>Thu, 26 Jan 2023 08:00:46 -0700</pubDate>
      
      <guid>/tinker/go-and-zig/</guid>
      <description><![CDATA[<p>In 2020 it became possible to very easily compile CGo using <a href="https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html" target="_blank" >zig&rsquo;s drop-in replacement for GCC/Clang</a>. For me, this is the biggest game changer in Go for a few years.</p>
<h2 id="windows-magic">windows magic</h2>
<p>on windows, you no longer need to install TDM-GCC or <a href="https://www.mingw-w64.org/" target="_blank" >MinGW-w64</a> to develop against CGo. <a href="https://scoop.sh/" target="_blank" >Using scoop</a> you can simply install zig:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="n">scoop</span> <span class="n">install</span> <span class="n">zig</span>
</span></span></code></pre></div><p>and then your CGo build will <em>just work</em> with the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="nv">$env:CGO_ENABLED</span><span class="p">=</span><span class="mf">1</span><span class="p">;</span> <span class="nv">$env:CC</span><span class="p">=</span><span class="s2">&#34;zig cc&#34;</span><span class="p">;</span> <span class="n">go</span> <span class="n">build</span> <span class="n">-v</span> <span class="n">-x</span>
</span></span></code></pre></div><p>I was having so many troubles with weird errors in MinGW-w64<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and this <em>just worked</em>.</p>
<h2 id="linux-magic">linux magic</h2>
<p>the linux situation is even more incredible.</p>
<p>one of the common things I want to do is cross-compile for windows from linux. <em>zig</em> also makes this super easy with <code>-target</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl"><span class="nv">CGO_ENABLED</span><span class="o">=</span><span class="m">1</span> <span class="nv">CC</span><span class="o">=</span><span class="s2">&#34;zig cc -target x86_64-windows-gnu&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a></span><span class="cl"><span class="se"></span>    <span class="nv">GOOS</span><span class="o">=</span>windows <span class="nv">GOARCH</span><span class="o">=</span>amd64 go build -v -x
</span></span></code></pre></div><p>even more magical is the Mac OSX situation, which was terribly riddled with strange errors and requires it&rsquo;s own SDK.
luckily, <a href="https://github.com/joseluisq/macosx-sdks" target="_blank" >Jose Quintana is hosting MacOS X SDK&rsquo;s</a> which can be used to compile against Mac OS X in a simple Makefile incantation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-make" data-lang="make"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a></span><span class="cl"><span class="nf">build</span><span class="o">:</span> <span class="n">MacOSX</span>11.3.<span class="n">sdk</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a></span><span class="cl">	<span class="nv">MACOS_MIN_VER</span><span class="o">=</span>11.3 <span class="nv">MACOS_SDK_PATH</span><span class="o">=</span><span class="k">$(</span>PWD<span class="k">)</span>/MacOSX11.3.sdk <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a></span><span class="cl"><span class="se"></span>        <span class="nv">CGO_ENABLED</span><span class="o">=</span><span class="m">1</span> <span class="nv">GOOS</span><span class="o">=</span>darwin <span class="nv">GOARCH</span><span class="o">=</span>arm64 <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a></span><span class="cl"><span class="se"></span>	<span class="nv">CGO_LDFLAGS</span><span class="o">=</span><span class="s2">&#34;-mmacosx-version-min=</span><span class="nv">$$</span><span class="s2">{MACOS_MIN_VER} --sysroot </span><span class="nv">$$</span><span class="s2">{MACOS_SDK_PATH} -F/System/Library/Frameworks -L/usr/lib&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a></span><span class="cl"><span class="se"></span>	<span class="nv">CC</span><span class="o">=</span><span class="s2">&#34;zig cc -target aarch64-macos -isysroot </span><span class="nv">$$</span><span class="s2">{MACOS_SDK_PATH} -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a></span><span class="cl"><span class="se"></span>	go build -ldflags <span class="s2">&#34;-s -w&#34;</span> -buildmode<span class="o">=</span>pie -v -x 
</span></span><span class="line"><span class="ln" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a></span><span class="cl"><span class="nf">MacOSX11.3.sdk</span><span class="o">:</span>
</span></span><span class="line"><span class="ln" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a></span><span class="cl">	wget https://github.com/joseluisq/macosx-sdks/releases/download/11.3/MacOSX11.3.sdk.tar.xz
</span></span><span class="line"><span class="ln" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a></span><span class="cl">	tar -xvf MacOSX11.3.sdk.tar.xz
</span></span></code></pre></div><p>the Mac OS X incantation itself is a little more involved because it has to grab the Frameworks library and I only learned about it from <a href="https://lucor.dev/post/cross-compile-golang-fyne-project-using-zig/" target="_blank" >Luca Corbo&rsquo;s post detailing it and other one-liners</a>. but again, it <em>just works</em>.</p>
<h2 id="github-actions">github actions</h2>
<p>the best for last - all of these tools work perfectly well in Github Actions. I am working on a project that is compiled for all OS&rsquo;s, using CGo, and <a href="https://github.com/schollz/_core/blob/main/.github/workflows/release.yml" target="_blank" >I now have a Github action</a> that automatically builds new releases.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I stopped trying to debug errors once I found this <code>zig</code> incantation, but here is the MinGW-w64 error I was getting: <code>error: '__format__' attribute argument not supported: gnu_scanf [-Werror,-Wignored-attributes]   __attribute__((__format__ (gnu_scanf, 2, 3))) __MINGW_ATTRIB_NONNULL(2)</code>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>zeptocore</title>
	    <image>https://schollz.com/img/zepto3.png</image>
      <link>/wares/zeptocore/</link>
      <pubDate>Fri, 20 Jan 2023 08:00:46 -0700</pubDate>
      
      <guid>/wares/zeptocore/</guid>
      <description><![CDATA[<h2 id="zeptocore-is">zeptocore is&hellip;</h2>
<ul>
<li>a <strong>mid-fi music mangler based on the Raspberry Pi Pico</strong> (brother of the <a href="/wares/nyblcore" >nyblcore</a> and the <a href="/wares/pikocore" >pikocore</a>).</li>
<li>mono or stereo playback of 16-bit (internal 32-bit) audio files @ 44.1 kHz sampling rate</li>
<li>powered by <strong>two AAA batteries for up to 4 hours or by powered by USB-C</strong></li>
<li><strong>tempo-synced</strong> with a selectable BPM between 60 and 300.</li>
<li>loaded with <strong>16 real-time effects</strong> like timestretching, delay, distortion, fuzz, tape stop, resonant filters, etc..</li>
<li><strong>sync-compatible</strong> with any gear that has a clock input/output signal.</li>
<li><a href="https://github.com/schollz/_core" target="_blank" >open-source</a>, wonderfully hackable.</li>
</ul>
<h2 id="buy">buy</h2>
<div id="mc_embed_shell" style="border-style: solid; border-color: #777; padding:1em; margin:1em; padding-top:0em; background: #fff; border-radius:1em;box-shadow: 12px 12px 2px 1px rgba(0, 0, 255, .2); margin-top:2em; margin-bottom:2em;">
<div id="mc_embed_signup">
    <form action="https://schollz.us21.list-manage.com/subscribe/post?u=6f305d82cde1a96e67a3c0577&amp;id=8f5c3ddc01&amp;f_id=0009ace1f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_self" novalidate="">
        <div id="mc_embed_signup_scroll"><h3>Sign up to receive an email when <span style="color:rgba(0, 0, 255, 0.6);weight:900;">zeptocore</span> is ready to buy:</h3>
            <div class="mc-field-group"><label for="mce-EMAIL" style="font-size:1em;font-weight:900;">Email Address: </label><input type="email" name="EMAIL" class="required email" id="mce-EMAIL" required="" value="" style="padding:0.5em; font-size:1em; font-family: 'Roboto Mono', monospace;  margin-right:1em;"><input type="submit" name="subscribe" id="mc-embedded-subscribe" class="button" value="Subscribe" style="font-family: 'Roboto Mono', monospace;padding:0.6em;"></div><div class="mc-field-group" style="display:none;"><label for="mce-ZEPTOCORE">WARES </label><input type="text" name="ZEPTOCORE" class=" text" id="mce-ZEPTOCORE" value="zeptocore"></div>
        <div id="mce-responses" class="clear">
            <div class="response" id="mce-error-response" style="display: none;"></div>
            <div class="response" id="mce-success-response" style="display: none;"></div>
        </div><div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_6f305d82cde1a96e67a3c0577_8f5c3ddc01" tabindex="-1" value="" ></div>
    </div>
</form>
</div>
</div>
<h2 id="tool">tool</h2>
<div id="mc_embed_shell" style="border-style: solid; border-color: #777; padding:1em; margin:1em; padding-top:0em; background: #fff; border-radius:1em;box-shadow: 12px 12px 2px 1px rgba(0, 0, 255, .2); margin-top:2em; margin-bottom:2em; text-align:center;">
<h2><a href="https://tool.zeptocore.com">click here</a> to use the zeptocore tool.</h2>
</div>
<h2 id="demos">demos</h2>
<p>still a wip&hellip;</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/l0G_VuOIfW8" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/PDYbv4IDOa4" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>Purchase Policy</title>
	    <image>https://schollz.com/img/purchase.jpg</image>
      <link>/wares/policy/</link>
      <pubDate>Sun, 26 Feb 2023 08:00:46 -0700</pubDate>
      
      <guid>/wares/policy/</guid>
      <description><![CDATA[<p><strong>Orders</strong></p>
<p>Orders are run through &ldquo;Shopify&rdquo;. The infinite digits store is part of my company &ldquo;<code>infinite digits</code>&rdquo; so when you make a purchase you will see credit card bills for &ldquo;<code>infinite digits</code>&rdquo; on your bills.</p>
<p><strong>Taxes</strong></p>
<p>International orders are subject to import taxes and duty upon arrival, which will be paid by the receiver. This is typically around 20% of the retail cost. The carrier will contact you when the item arrives in customs to arrange payment. If this payment is refused and the package is returned, we will be forced to pay this duty and return shipping, which will be deducted from your refund.</p>
<p><strong>Cancellations</strong></p>
<p>Orders which have not yet shipped may be canceled any time before shipment for a full refund minus a 2.6% + $0.30 (US) or 3.6% + $0.30 (international) processing fee. Refunds must be sent to the original form of payment.</p>
<p><strong>Shipping &amp; Delivery</strong></p>
<p>We use &ldquo;Shopify&rdquo; to send both within USA and internationally. The price for shipping therefore varies depending on where in the world it is to be sent. This appears at check-out and a confirmation is also sent via email when the order is sent. In most cases, tracking of packages is included and you as a customer will then also receive a tracking link on the email you enter when purchasing. Orders are shipped within three business days.</p>
<p><strong>Returns</strong></p>
<p>Within 7 days of delivery, email <a href="mailto:zack@infinitedigits.co" >zack@infinitedigits.co</a> to arrange a return. If you’re in the US, I&rsquo;ll email you a USPS return label. if you’re outside of the US, I&rsquo;ll ask you to arrange return delivery using your preferred international shipping provider.</p>
<p>Re-pack items in their original packaging, as you received them. Once I receive the return, any item(s) in new/unused condition will receive a full refund (excluding any shipping costs and minus a 2.6% + $0.30 (for US) or 3.6% + $0.30 (for international) processing fee).</p>
<p><strong>Stock</strong></p>
<p>Devices are made in small batches and are built up in the capacity they are built. Therefore, only the units listed on the website are in the quantity listed on the website. The stock is updated with each completed order. If an item is on the page but is eventually out of stock, this means that it will be updated as soon as it is built. No pre-purchases can be made, units are only sold when they are ready and in stock.</p>
<p><strong>Warranty</strong></p>
<p>If your device is not working as intended, please feel free to contact me at <a href="mailto:zack@infinitedigits.co" >zack@infinitedigits.co</a> to swap the item or arrange a refund.  Every device is backed by a one year warranty to cover manufacturing failure (shipping costs not covered).</p>
<p>If any device fails during regular use within one year of purchase by the original owner, please let me know as soon as it happens by emailing <a href="mailto:zack@infinitedigits.co" >zack@infinitedigits.co</a>. Be sure to include your order number and a description of the problem.</p>
]]></description>
    </item>
    
    <item>
	    <title>sd cards</title>
	    <image>https://schollz.com/img/sdcard.png</image>
      <link>/tinker/sdcard/</link>
      <pubDate>Wed, 03 Jan 2024 08:00:46 -0700</pubDate>
      
      <guid>/tinker/sdcard/</guid>
      <description><![CDATA[<p>I am working on an embedded device which I call &ldquo;<a href="https://github.com/schollz/_core" target="_blank" >_core</a>&rdquo;. One of the main features of this device is the ability to jump around in an audio sample. Jumping around in audio normally requires a computer to load in the entire audio data into RAM. However, 16-bit audio at 44.1 khz quickly becomes megabytes of data and I&rsquo;m using the rp2040 which only has 264 kilobytes of internal RAM! So instead of using RAM, I&rsquo;m attempting to use an SD Card to quickly and randomly access memory.</p>
<h2 id="sd-card-in-the-audio-block">SD card in the audio block</h2>
<p>In many applications (mine included), audio is transfered in &ldquo;blocks&rdquo;, i.e. the audio dac asks for a batch of samples all at once instead of one sample at a time. For real-time audio this is a good strategy to buffer playback so that a sample isn&rsquo;t lost in case another sample takes too long to compute. In my audio application the audio block consists of 441 samples. This means, at a sampling rate of 44,100 samples/second (44.1 khz), there is only 10 milliseconds to generate the next piece of audio.</p>
<p>If the audio data is in RAM, there is almost no latency to access the data. However, when the audio is coming from the SD card, then there is some latency involved in sending messages to the SD card. To prevent dropouts (bad audio &ldquo;pops&rdquo;), the SD card must succesfully retrieve data in less than 10 milliseconds.</p>
<p>So my most basic question is &ldquo;what is the best SD card to use&rdquo; which revolves around two questions:</p>
<ol>
<li>How fast can a SD card retrieve audio data in my application?</li>
<li>How often does the SD card require more than 10 milliseconds to retrieve data?</li>
</ol>
<h2 id="experiment">Experiment</h2>
<p>I bought 14 different SD cards and tested them using the same firmware, same hardware, and same samples. My firmware uses the SDIO protocol, for this test it is accessing 819 bytes after seeking to a position. I use the rp2040 <code>time_us_64()</code> to get the timing before and after the operations, and add up the entire time to get the latency.</p>
<a href="/img/sdcard_timings.png" target="_blank">
<img src="/img/sdcard_timings.png">
</a>
<p>(click the image to enlarge). The box-plots show the median read duration (latency) in my application and the dots show the outliers. The red area at the top shows where latency exceeds the audio block time, causing dropouts. (<a href="https://github.com/schollz/_core/blob/main/dev/analyze_sdcards.py" target="_blank" >code for making plot</a>)</p>
<h2 id="experiment-2-overclocking">Experiment 2 (overclocking)</h2>
<p>A few weeks later, I decided that I need to run my rp2040 at 250 Mhz instead of 125 Mhz to eek out much more CPU processing power. The SDIO does not run this fast, so I am increasing the clock divider from 1 to 2 so that the SDIO actually runs at the same speed.</p>
<a href="/img/sdcard_timings2.png" target="_blank">
<img src="/img/sdcard_timings2.png">
</a>
<p>(click the image to enlarge). In this case we see partially the same story - &ldquo;Lexar&rdquo; is fast but unreliable. But also now we can see that &ldquo;Kootion&rdquo; is fast and unreliable as well. However, interestingly, the &ldquo;PNY&rdquo; and the &ldquo;SP Elite&rdquo; chips which used to be unreliable are now more reliable and their speed matches most of the other cards. The slow card here is, again, the SanDisk Ultra 16GB, which is a bit old. Also the &ldquo;Kingston 2GB&rdquo; is apparently so old that the SDIO never worked on it at all.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I found that most SD cards are pretty fast. There are some that are much slower (an old Sandisk 16GB). Surprisingly, the fastest SD card (Lexar A1 32 GB) is not usable because it has periodic spikes of high latency which causes audio dropouts.</p>
<p>The best SD card then is somewhere in the middle - not too fast and mostly reliable!</p>
<p><em>not all SD cards are the same!</em></p>
]]></description>
    </item>
    
    <item>
	    <title>gno -&gt; go</title>
	    <image>https://schollz.com/img/gno.png</image>
      <link>/tinker/web3/</link>
      <pubDate>Sat, 02 Dec 2023 08:00:46 -0700</pubDate>
      
      <guid>/tinker/web3/</guid>
      <description><![CDATA[<p>I&rsquo;ve developed <a href="/tags/websites" >dozens of websites using Go</a> and recently I&rsquo;ve been drawn by my curiosity into how website/app development would be done with &ldquo;web3&rdquo; technologies. Luckily, there is a new language called &ldquo;<a href="https://github.com/gnolang/gno" target="_blank" >Gno</a>&rdquo; that is lowering the barrier to entry for developing in web3.</p>
<h3 id="what-is-gno">what is gno?</h3>
<p>Gno isn&rsquo;t just another programming language; it&rsquo;s an interpreted version of the Go language, optimized for the intricacies of blockchain and deterministic execution on distributed systems. If you&rsquo;re familiar with Go, transitioning to Gno is a breeze.</p>
<p>Here are some key differentiators between Go and Gno:</p>
<ul>
<li><strong>Special Packages</strong>: Gno introduces specialized packages like std for accessing the caller&rsquo;s address and <code>avl.Tree</code> for a deterministic map.</li>
<li><strong>Deterministic Design</strong>: Gno execution is verifiable and is capable of running on distributed systems.</li>
<li><strong>Transpilation Magic</strong>: Under the hood, Gno code is transpiled into Go, leveraging the robust Go compiler system.</li>
</ul>
<p>The differences may seem minute, but they pave the way for powerful blockchain applications with unparalleled precision.</p>
<p>There is a lot more information on their main website at <a href="https://gno.land" target="_blank" >gno.land</a>. Also you can just easily play with Gno code online too, at the <a href="https://play.gno.land/" target="_blank" >play.gno.land website</a>.</p>
<h3 id="what-is-a-smart-contract">What is a Smart Contract?</h3>
<p>&ldquo;Smart contracts&rdquo; are are immutable, self-executing programs living on the blockchain.</p>
<p>Beyond automating transactions, smart contracts can be used for various applications, such as creating incentivized social networks and reshaping interactions with the web (i.e., &ldquo;web3&rdquo;). Smart contracts written in Gno run within the Gno.land ecosystem.</p>
<h3 id="what-is-gnoland">What is Gno.land?</h3>
<p><a href="https://gno.land" target="_blank" >Gno.land</a> is a platform for writing smart contracts in Gno, the first of a series of Gno Layer 1 chains. Built on Tendermint2, Cosmos/IBC, and secured by Proof of Contribution, Gno.land prioritizes simplicity, security, scalability, and transparency.</p>
<h2 id="web3-bytebeat-symphony">web3 Bytebeat Symphony</h2>
<p>For my first attempt into web3 I wanted to make a simple, shareable system of music. Music could be encoded into the blockchain and it could be easily recorded (verifiably) that some piece of music was generated first by some identity on the chain. I went for <a href="http://canonical.org/~kragen/bytebeat/" target="_blank" >bytebeat</a> music because its very straightforward to code. bytebeat was discovered by <a href="http://countercomplex.blogspot.com/2015/04/bringing-magic-back-to-technology.html" target="_blank" >viznut</a> in 2011 as a way to type a very short computer programs that generate chiptune music, for example this is a bytebeat program:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="n">t</span><span class="p">){</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl">  <span class="k">for</span><span class="p">(;;</span><span class="n">t</span><span class="o">++</span><span class="p">)</span> <span class="nf">putchar</span><span class="p">(((</span><span class="n">t</span><span class="o">&lt;&lt;</span><span class="mi">1</span><span class="p">)</span><span class="o">^</span><span class="p">((</span><span class="n">t</span><span class="o">&lt;&lt;</span><span class="mi">1</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="n">t</span><span class="o">&gt;&gt;</span><span class="mi">7</span><span class="p">)</span><span class="o">&amp;</span><span class="n">t</span><span class="o">&gt;&gt;</span><span class="mi">12</span><span class="p">)));</span>
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>which you can take the raw output of and convert to audio:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">gcc -o crowd crowd.c
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl">./crowd <span class="p">|</span> head -c 4M &gt; crowd.raw
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl">sox -r <span class="m">8000</span> -c <span class="m">1</span> -t u8 crowd.raw crowd.wav
</span></span></code></pre></div><h3 id="simplicity-built-in">simplicity, built-in</h3>
<p>The thing that amazed me about the Gno language is <em>how fast</em> it is to get started. First of all, the language is basically Go, plus a few addons. Also its mostly &ldquo;batteries included&rdquo; (persistent globals = database, getting callers = identification). Here is, essentially, my entire &ldquo;smart contract&rdquo; for generating bytebeat music, in just 50 lines of code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">bytebeat</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl">	<span class="s">&#34;std&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl">	<span class="nx">bytebeat</span> <span class="s">&#34;gno.land/p/demo/audio/bytebeat/v1&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl">	<span class="s">&#34;gno.land/p/demo/ufmt&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl"><span class="kd">type</span> <span class="nx">Comment</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl">	<span class="nx">User</span>    <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl">	<span class="nx">Message</span> <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a></span><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a></span><span class="cl">	<span class="nx">data</span>     <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a></span><span class="cl">	<span class="nx">comments</span> <span class="p">[]</span><span class="nx">Comment</span>
</span></span><span class="line"><span class="ln" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a></span><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-21"><a class="lnlinks" href="#hl-2-21">21</a></span><span class="cl">	<span class="nx">seconds</span> <span class="o">:=</span> <span class="nb">uint32</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-22"><a class="lnlinks" href="#hl-2-22">22</a></span><span class="cl">	<span class="nx">data</span> <span class="p">=</span> <span class="nx">bytebeat</span><span class="p">.</span><span class="nf">ByteBeat</span><span class="p">(</span><span class="nx">seconds</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-23"><a class="lnlinks" href="#hl-2-23">23</a></span><span class="cl">		<span class="k">return</span> <span class="p">(</span><span class="nx">t</span><span class="o">&gt;&gt;</span><span class="mi">10</span><span class="p">^</span><span class="nx">t</span><span class="o">&gt;&gt;</span><span class="mi">11</span><span class="p">)</span><span class="o">%</span><span class="mi">5</span><span class="o">*</span><span class="p">((</span><span class="nx">t</span><span class="o">&gt;&gt;</span><span class="mi">14</span><span class="o">&amp;</span><span class="mi">3</span><span class="p">^</span><span class="nx">t</span><span class="o">&gt;&gt;</span><span class="mi">15</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="nx">t</span><span class="o">%</span><span class="mi">99</span> <span class="o">+</span> <span class="p">((</span><span class="mi">3</span> <span class="o">+</span> <span class="p">(</span><span class="nx">t</span> <span class="o">&gt;&gt;</span> <span class="mi">14</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="nx">t</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="mi">3</span> <span class="o">*</span> <span class="nx">t</span> <span class="o">%</span> <span class="mi">99</span> <span class="o">&amp;</span> <span class="mi">64</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-24"><a class="lnlinks" href="#hl-2-24">24</a></span><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-2-25"><a class="lnlinks" href="#hl-2-25">25</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-26"><a class="lnlinks" href="#hl-2-26">26</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-27"><a class="lnlinks" href="#hl-2-27">27</a></span><span class="cl"><span class="kd">func</span> <span class="nf">AddComment</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-28"><a class="lnlinks" href="#hl-2-28">28</a></span><span class="cl">	<span class="nx">caller</span> <span class="o">:=</span> <span class="nx">std</span><span class="p">.</span><span class="nf">GetOrigCaller</span><span class="p">()</span> <span class="c1">// main
</span></span></span><span class="line"><span class="ln" id="hl-2-29"><a class="lnlinks" href="#hl-2-29">29</a></span><span class="cl"><span class="c1"></span>	<span class="nx">comments</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">comments</span><span class="p">,</span> <span class="nx">Comment</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-30"><a class="lnlinks" href="#hl-2-30">30</a></span><span class="cl">		<span class="nx">User</span><span class="p">:</span>    <span class="nb">string</span><span class="p">(</span><span class="nx">caller</span><span class="p">),</span>
</span></span><span class="line"><span class="ln" id="hl-2-31"><a class="lnlinks" href="#hl-2-31">31</a></span><span class="cl">		<span class="nx">Message</span><span class="p">:</span> <span class="nx">s</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-32"><a class="lnlinks" href="#hl-2-32">32</a></span><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-2-33"><a class="lnlinks" href="#hl-2-33">33</a></span><span class="cl">	<span class="k">return</span> <span class="s">&#34;ok&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-34"><a class="lnlinks" href="#hl-2-34">34</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-35"><a class="lnlinks" href="#hl-2-35">35</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-36"><a class="lnlinks" href="#hl-2-36">36</a></span><span class="cl"><span class="kd">func</span> <span class="nf">Render</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-37"><a class="lnlinks" href="#hl-2-37">37</a></span><span class="cl">	<span class="nx">output</span> <span class="o">:=</span> <span class="s">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-38"><a class="lnlinks" href="#hl-2-38">38</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="s">&#34;\n&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-39"><a class="lnlinks" href="#hl-2-39">39</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="s">&#34;# my bytebeat\n&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-40"><a class="lnlinks" href="#hl-2-40">40</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="s">`&lt;audio controls=&#34;controls&#34; autobuffer=&#34;autobuffer&#34; autoplay=&#34;autoplay&#34;&gt;
</span></span></span><span class="line"><span class="ln" id="hl-2-41"><a class="lnlinks" href="#hl-2-41">41</a></span><span class="cl"><span class="s">&lt;source src=&#34;data:audio/wav;base64,`</span>
</span></span><span class="line"><span class="ln" id="hl-2-42"><a class="lnlinks" href="#hl-2-42">42</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="nx">data</span>
</span></span><span class="line"><span class="ln" id="hl-2-43"><a class="lnlinks" href="#hl-2-43">43</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="s">`&#34; /&gt;
</span></span></span><span class="line"><span class="ln" id="hl-2-44"><a class="lnlinks" href="#hl-2-44">44</a></span><span class="cl"><span class="s">&lt;/audio&gt;`</span>
</span></span><span class="line"><span class="ln" id="hl-2-45"><a class="lnlinks" href="#hl-2-45">45</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="s">&#34;\n\n## comments\n&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-46"><a class="lnlinks" href="#hl-2-46">46</a></span><span class="cl">	<span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">comment</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">comments</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-47"><a class="lnlinks" href="#hl-2-47">47</a></span><span class="cl">		<span class="nx">output</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%d. *%s* - %s\n &#34;</span><span class="p">,</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">User</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-48"><a class="lnlinks" href="#hl-2-48">48</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-49"><a class="lnlinks" href="#hl-2-49">49</a></span><span class="cl">	<span class="k">return</span> <span class="nx">output</span>
</span></span><span class="line"><span class="ln" id="hl-2-50"><a class="lnlinks" href="#hl-2-50">50</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>There are some clever built-in functions here, like the <code>Render</code> function is automatically used by gno.land to do web rendering - so in addition to a smart contract you get a web server for free.</p>
<p>This smart contract is not yet totally &ldquo;in action&rdquo; yet because there is no main net to deliver it. But it is really straightforward and easy to setup your own Gno.land server to run smart contracts. I wrote a whole presentation on the topic which you can read more about <a href="https://github.com/gnolang/workshops/blob/main/presentations/2023-10-09--generating-audio--schollz/2023-10-09--generating-audio--schollz.pdf" target="_blank" >here</a>.</p>
<h2 id="microblogging-in-the-gnoverse">Microblogging in the GnoVerse</h2>
<p>For another attempt into web3 I was interested in making a Twitter-like thing - essentially a way to &ldquo;microblog&rdquo;. Again, my smart contract for this package is extremely concise:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">microblog</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a></span><span class="cl">	<span class="s">&#34;std&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a></span><span class="cl">	<span class="s">&#34;gno.land/p/demo/microblog&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a></span><span class="cl">	<span class="s">&#34;gno.land/r/demo/users&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a></span><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a></span><span class="cl">	<span class="nx">title</span>  <span class="p">=</span> <span class="s">&#34;gno-based microblog&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a></span><span class="cl">	<span class="nx">prefix</span> <span class="p">=</span> <span class="s">&#34;/r/demo/microblog:&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a></span><span class="cl">	<span class="nx">m</span>      <span class="o">*</span><span class="nx">microblog</span><span class="p">.</span><span class="nx">Microblog</span>
</span></span><span class="line"><span class="ln" id="hl-3-14"><a class="lnlinks" href="#hl-3-14">14</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-15"><a class="lnlinks" href="#hl-3-15">15</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-16"><a class="lnlinks" href="#hl-3-16">16</a></span><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-17"><a class="lnlinks" href="#hl-3-17">17</a></span><span class="cl">	<span class="nx">m</span> <span class="p">=</span> <span class="nx">microblog</span><span class="p">.</span><span class="nf">NewMicroblog</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span> <span class="nx">prefix</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-18"><a class="lnlinks" href="#hl-3-18">18</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-3-19"><a class="lnlinks" href="#hl-3-19">19</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-20"><a class="lnlinks" href="#hl-3-20">20</a></span><span class="cl"><span class="c1">// Render calls the microblog renderer
</span></span></span><span class="line"><span class="ln" id="hl-3-21"><a class="lnlinks" href="#hl-3-21">21</a></span><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Render</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-22"><a class="lnlinks" href="#hl-3-22">22</a></span><span class="cl">	<span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nf">Render</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-23"><a class="lnlinks" href="#hl-3-23">23</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-3-24"><a class="lnlinks" href="#hl-3-24">24</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-25"><a class="lnlinks" href="#hl-3-25">25</a></span><span class="cl"><span class="c1">// NewPost takes a single argument (post markdown) and
</span></span></span><span class="line"><span class="ln" id="hl-3-26"><a class="lnlinks" href="#hl-3-26">26</a></span><span class="cl"><span class="c1">// adds a post to the address of the caller.
</span></span></span><span class="line"><span class="ln" id="hl-3-27"><a class="lnlinks" href="#hl-3-27">27</a></span><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewPost</span><span class="p">(</span><span class="nx">text</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-28"><a class="lnlinks" href="#hl-3-28">28</a></span><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">NewPost</span><span class="p">(</span><span class="nx">text</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-29"><a class="lnlinks" href="#hl-3-29">29</a></span><span class="cl">		<span class="k">return</span> <span class="s">&#34;unable to add new post&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-30"><a class="lnlinks" href="#hl-3-30">30</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-3-31"><a class="lnlinks" href="#hl-3-31">31</a></span><span class="cl">	<span class="k">return</span> <span class="s">&#34;added new post&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-32"><a class="lnlinks" href="#hl-3-32">32</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-3-33"><a class="lnlinks" href="#hl-3-33">33</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-34"><a class="lnlinks" href="#hl-3-34">34</a></span><span class="cl"><span class="kd">func</span> <span class="nf">Register</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">profile</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-35"><a class="lnlinks" href="#hl-3-35">35</a></span><span class="cl">	<span class="nx">caller</span> <span class="o">:=</span> <span class="nx">std</span><span class="p">.</span><span class="nf">GetOrigCaller</span><span class="p">()</span> <span class="c1">// main
</span></span></span><span class="line"><span class="ln" id="hl-3-36"><a class="lnlinks" href="#hl-3-36">36</a></span><span class="cl"><span class="c1"></span>	<span class="nx">users</span><span class="p">.</span><span class="nf">Register</span><span class="p">(</span><span class="nx">caller</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">profile</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-37"><a class="lnlinks" href="#hl-3-37">37</a></span><span class="cl">	<span class="k">return</span> <span class="s">&#34;OK&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-38"><a class="lnlinks" href="#hl-3-38">38</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A lot of work is being done behind the scenes here with some &ldquo;packages&rdquo;. There is a difference between &ldquo;Packages&rdquo; and &ldquo;Realms&rdquo; in Gno - where both are like Go packages in that they are modular and importable, but &ldquo;Realms&rdquo; have the special ability that they persist globals, as they are meant to be run on the block chain.</p>
<p>The package for the microblog is the most important and uses a lot of the cool Gno features. There is a AVL tree for managing data and some user management properties built into the language:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">  1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">microblog</span>
</span></span><span class="line"><span class="ln" id="hl-4-2"><a class="lnlinks" href="#hl-4-2">  2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-3"><a class="lnlinks" href="#hl-4-3">  3</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-4-4"><a class="lnlinks" href="#hl-4-4">  4</a></span><span class="cl">	<span class="s">&#34;errors&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-5"><a class="lnlinks" href="#hl-4-5">  5</a></span><span class="cl">	<span class="s">&#34;sort&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-6"><a class="lnlinks" href="#hl-4-6">  6</a></span><span class="cl">	<span class="s">&#34;std&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-7"><a class="lnlinks" href="#hl-4-7">  7</a></span><span class="cl">	<span class="s">&#34;strings&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-8"><a class="lnlinks" href="#hl-4-8">  8</a></span><span class="cl">	<span class="s">&#34;time&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-9"><a class="lnlinks" href="#hl-4-9">  9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-10"><a class="lnlinks" href="#hl-4-10"> 10</a></span><span class="cl">	<span class="s">&#34;gno.land/p/demo/avl&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-11"><a class="lnlinks" href="#hl-4-11"> 11</a></span><span class="cl">	<span class="s">&#34;gno.land/p/demo/ufmt&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-12"><a class="lnlinks" href="#hl-4-12"> 12</a></span><span class="cl">	<span class="s">&#34;gno.land/r/demo/users&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-13"><a class="lnlinks" href="#hl-4-13"> 13</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-14"><a class="lnlinks" href="#hl-4-14"> 14</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-15"><a class="lnlinks" href="#hl-4-15"> 15</a></span><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-4-16"><a class="lnlinks" href="#hl-4-16"> 16</a></span><span class="cl">	<span class="nx">ErrNotFound</span>    <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;not found&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-17"><a class="lnlinks" href="#hl-4-17"> 17</a></span><span class="cl">	<span class="nx">StatusNotFound</span> <span class="p">=</span> <span class="s">&#34;404&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-18"><a class="lnlinks" href="#hl-4-18"> 18</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-19"><a class="lnlinks" href="#hl-4-19"> 19</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-20"><a class="lnlinks" href="#hl-4-20"> 20</a></span><span class="cl"><span class="kd">type</span> <span class="nx">Microblog</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-21"><a class="lnlinks" href="#hl-4-21"> 21</a></span><span class="cl">	<span class="nx">Title</span>  <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-4-22"><a class="lnlinks" href="#hl-4-22"> 22</a></span><span class="cl">	<span class="nx">Prefix</span> <span class="kt">string</span>   <span class="c1">// i.e. r/gnoland/blog:
</span></span></span><span class="line"><span class="ln" id="hl-4-23"><a class="lnlinks" href="#hl-4-23"> 23</a></span><span class="cl"><span class="c1"></span>	<span class="nx">Pages</span>  <span class="nx">avl</span><span class="p">.</span><span class="nx">Tree</span> <span class="c1">// author (string) -&gt; Page
</span></span></span><span class="line"><span class="ln" id="hl-4-24"><a class="lnlinks" href="#hl-4-24"> 24</a></span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-25"><a class="lnlinks" href="#hl-4-25"> 25</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-26"><a class="lnlinks" href="#hl-4-26"> 26</a></span><span class="cl"><span class="kd">func</span> <span class="nf">NewMicroblog</span><span class="p">(</span><span class="nx">title</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">prefix</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-27"><a class="lnlinks" href="#hl-4-27"> 27</a></span><span class="cl">	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">Microblog</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-28"><a class="lnlinks" href="#hl-4-28"> 28</a></span><span class="cl">		<span class="nx">Title</span><span class="p">:</span>  <span class="nx">title</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-29"><a class="lnlinks" href="#hl-4-29"> 29</a></span><span class="cl">		<span class="nx">Prefix</span><span class="p">:</span> <span class="nx">prefix</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-30"><a class="lnlinks" href="#hl-4-30"> 30</a></span><span class="cl">		<span class="nx">Pages</span><span class="p">:</span>  <span class="nx">avl</span><span class="p">.</span><span class="nx">Tree</span><span class="p">{},</span>
</span></span><span class="line"><span class="ln" id="hl-4-31"><a class="lnlinks" href="#hl-4-31"> 31</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-32"><a class="lnlinks" href="#hl-4-32"> 32</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-33"><a class="lnlinks" href="#hl-4-33"> 33</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-34"><a class="lnlinks" href="#hl-4-34"> 34</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="nf">GetPages</span><span class="p">()</span> <span class="p">[]</span><span class="o">*</span><span class="nx">Page</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-35"><a class="lnlinks" href="#hl-4-35"> 35</a></span><span class="cl">	<span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-4-36"><a class="lnlinks" href="#hl-4-36"> 36</a></span><span class="cl">		<span class="nx">pages</span> <span class="p">=</span> <span class="nb">make</span><span class="p">([]</span><span class="o">*</span><span class="nx">Page</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Pages</span><span class="p">.</span><span class="nf">Size</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-37"><a class="lnlinks" href="#hl-4-37"> 37</a></span><span class="cl">		<span class="nx">index</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-4-38"><a class="lnlinks" href="#hl-4-38"> 38</a></span><span class="cl">	<span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-39"><a class="lnlinks" href="#hl-4-39"> 39</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-40"><a class="lnlinks" href="#hl-4-40"> 40</a></span><span class="cl">	<span class="nx">m</span><span class="p">.</span><span class="nx">Pages</span><span class="p">.</span><span class="nf">Iterate</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">value</span> <span class="kd">interface</span><span class="p">{})</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-41"><a class="lnlinks" href="#hl-4-41"> 41</a></span><span class="cl">		<span class="nx">pages</span><span class="p">[</span><span class="nx">index</span><span class="p">]</span> <span class="p">=</span> <span class="nx">value</span><span class="p">.(</span><span class="o">*</span><span class="nx">Page</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-42"><a class="lnlinks" href="#hl-4-42"> 42</a></span><span class="cl">		<span class="nx">index</span><span class="o">++</span>
</span></span><span class="line"><span class="ln" id="hl-4-43"><a class="lnlinks" href="#hl-4-43"> 43</a></span><span class="cl">		<span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln" id="hl-4-44"><a class="lnlinks" href="#hl-4-44"> 44</a></span><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-4-45"><a class="lnlinks" href="#hl-4-45"> 45</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-46"><a class="lnlinks" href="#hl-4-46"> 46</a></span><span class="cl">	<span class="nx">sort</span><span class="p">.</span><span class="nf">Sort</span><span class="p">(</span><span class="nf">byLastPosted</span><span class="p">(</span><span class="nx">pages</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-47"><a class="lnlinks" href="#hl-4-47"> 47</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-48"><a class="lnlinks" href="#hl-4-48"> 48</a></span><span class="cl">	<span class="k">return</span> <span class="nx">pages</span>
</span></span><span class="line"><span class="ln" id="hl-4-49"><a class="lnlinks" href="#hl-4-49"> 49</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-50"><a class="lnlinks" href="#hl-4-50"> 50</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-51"><a class="lnlinks" href="#hl-4-51"> 51</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="nf">RenderHome</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-52"><a class="lnlinks" href="#hl-4-52"> 52</a></span><span class="cl">	<span class="nx">output</span> <span class="o">:=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;# %s\n\n&#34;</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Title</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-53"><a class="lnlinks" href="#hl-4-53"> 53</a></span><span class="cl">	<span class="nx">output</span> <span class="o">+=</span> <span class="s">&#34;# pages\n\n&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-54"><a class="lnlinks" href="#hl-4-54"> 54</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-55"><a class="lnlinks" href="#hl-4-55"> 55</a></span><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">page</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">m</span><span class="p">.</span><span class="nf">GetPages</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-56"><a class="lnlinks" href="#hl-4-56"> 56</a></span><span class="cl">		<span class="k">if</span> <span class="nx">u</span> <span class="o">:=</span> <span class="nx">users</span><span class="p">.</span><span class="nf">GetUserByAddress</span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">Author</span><span class="p">);</span> <span class="nx">u</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-57"><a class="lnlinks" href="#hl-4-57"> 57</a></span><span class="cl">			<span class="nx">output</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;- [%s (%s)](%s%s)\n&#34;</span><span class="p">,</span> <span class="nx">u</span><span class="p">.</span><span class="nf">Name</span><span class="p">(),</span> <span class="nx">page</span><span class="p">.</span><span class="nx">Author</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Prefix</span><span class="p">,</span> <span class="nx">page</span><span class="p">.</span><span class="nx">Author</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-58"><a class="lnlinks" href="#hl-4-58"> 58</a></span><span class="cl">		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-59"><a class="lnlinks" href="#hl-4-59"> 59</a></span><span class="cl">			<span class="nx">output</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;- [%s](%s%s)\n&#34;</span><span class="p">,</span> <span class="nx">page</span><span class="p">.</span><span class="nx">Author</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Prefix</span><span class="p">,</span> <span class="nx">page</span><span class="p">.</span><span class="nx">Author</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-60"><a class="lnlinks" href="#hl-4-60"> 60</a></span><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-61"><a class="lnlinks" href="#hl-4-61"> 61</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-62"><a class="lnlinks" href="#hl-4-62"> 62</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-63"><a class="lnlinks" href="#hl-4-63"> 63</a></span><span class="cl">	<span class="k">return</span> <span class="nx">output</span>
</span></span><span class="line"><span class="ln" id="hl-4-64"><a class="lnlinks" href="#hl-4-64"> 64</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-65"><a class="lnlinks" href="#hl-4-65"> 65</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-66"><a class="lnlinks" href="#hl-4-66"> 66</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="nf">RenderUser</span><span class="p">(</span><span class="nx">user</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-67"><a class="lnlinks" href="#hl-4-67"> 67</a></span><span class="cl">	<span class="nx">silo</span><span class="p">,</span> <span class="nx">found</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Pages</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-68"><a class="lnlinks" href="#hl-4-68"> 68</a></span><span class="cl">	<span class="k">if</span> <span class="p">!</span><span class="nx">found</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-69"><a class="lnlinks" href="#hl-4-69"> 69</a></span><span class="cl">		<span class="k">return</span> <span class="nx">StatusNotFound</span>
</span></span><span class="line"><span class="ln" id="hl-4-70"><a class="lnlinks" href="#hl-4-70"> 70</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-71"><a class="lnlinks" href="#hl-4-71"> 71</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-72"><a class="lnlinks" href="#hl-4-72"> 72</a></span><span class="cl">	<span class="k">return</span> <span class="p">(</span><span class="nx">silo</span><span class="p">.(</span><span class="o">*</span><span class="nx">Page</span><span class="p">)).</span><span class="nf">String</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-73"><a class="lnlinks" href="#hl-4-73"> 73</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-74"><a class="lnlinks" href="#hl-4-74"> 74</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-75"><a class="lnlinks" href="#hl-4-75"> 75</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="nf">Render</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-76"><a class="lnlinks" href="#hl-4-76"> 76</a></span><span class="cl">	<span class="nx">parts</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="s">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-77"><a class="lnlinks" href="#hl-4-77"> 77</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-78"><a class="lnlinks" href="#hl-4-78"> 78</a></span><span class="cl">	<span class="nx">isHome</span> <span class="o">:=</span> <span class="nx">path</span> <span class="o">==</span> <span class="s">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-79"><a class="lnlinks" href="#hl-4-79"> 79</a></span><span class="cl">	<span class="nx">isUser</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">parts</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln" id="hl-4-80"><a class="lnlinks" href="#hl-4-80"> 80</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-81"><a class="lnlinks" href="#hl-4-81"> 81</a></span><span class="cl">	<span class="k">switch</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-82"><a class="lnlinks" href="#hl-4-82"> 82</a></span><span class="cl">	<span class="k">case</span> <span class="nx">isHome</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-4-83"><a class="lnlinks" href="#hl-4-83"> 83</a></span><span class="cl">		<span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nf">RenderHome</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-84"><a class="lnlinks" href="#hl-4-84"> 84</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-85"><a class="lnlinks" href="#hl-4-85"> 85</a></span><span class="cl">	<span class="k">case</span> <span class="nx">isUser</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-4-86"><a class="lnlinks" href="#hl-4-86"> 86</a></span><span class="cl">		<span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nf">RenderUser</span><span class="p">(</span><span class="nx">parts</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="ln" id="hl-4-87"><a class="lnlinks" href="#hl-4-87"> 87</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-88"><a class="lnlinks" href="#hl-4-88"> 88</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-89"><a class="lnlinks" href="#hl-4-89"> 89</a></span><span class="cl">	<span class="k">return</span> <span class="nx">StatusNotFound</span>
</span></span><span class="line"><span class="ln" id="hl-4-90"><a class="lnlinks" href="#hl-4-90"> 90</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-91"><a class="lnlinks" href="#hl-4-91"> 91</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-92"><a class="lnlinks" href="#hl-4-92"> 92</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="nf">NewPost</span><span class="p">(</span><span class="nx">text</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-93"><a class="lnlinks" href="#hl-4-93"> 93</a></span><span class="cl">	<span class="nx">author</span> <span class="o">:=</span> <span class="nx">std</span><span class="p">.</span><span class="nf">GetOrigCaller</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-94"><a class="lnlinks" href="#hl-4-94"> 94</a></span><span class="cl">	<span class="nx">_</span><span class="p">,</span> <span class="nx">found</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Pages</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">author</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-95"><a class="lnlinks" href="#hl-4-95"> 95</a></span><span class="cl">	<span class="k">if</span> <span class="p">!</span><span class="nx">found</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-96"><a class="lnlinks" href="#hl-4-96"> 96</a></span><span class="cl">		<span class="c1">// make a new page for the new author
</span></span></span><span class="line"><span class="ln" id="hl-4-97"><a class="lnlinks" href="#hl-4-97"> 97</a></span><span class="cl"><span class="c1"></span>		<span class="nx">m</span><span class="p">.</span><span class="nx">Pages</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="nx">author</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="o">&amp;</span><span class="nx">Page</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-98"><a class="lnlinks" href="#hl-4-98"> 98</a></span><span class="cl">			<span class="nx">Author</span><span class="p">:</span>    <span class="nx">author</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-99"><a class="lnlinks" href="#hl-4-99"> 99</a></span><span class="cl">			<span class="nx">CreatedAt</span><span class="p">:</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln" id="hl-4-100"><a class="lnlinks" href="#hl-4-100">100</a></span><span class="cl">		<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-4-101"><a class="lnlinks" href="#hl-4-101">101</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-102"><a class="lnlinks" href="#hl-4-102">102</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-103"><a class="lnlinks" href="#hl-4-103">103</a></span><span class="cl">	<span class="nx">page</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">GetPage</span><span class="p">(</span><span class="nx">author</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-104"><a class="lnlinks" href="#hl-4-104">104</a></span><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-105"><a class="lnlinks" href="#hl-4-105">105</a></span><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="ln" id="hl-4-106"><a class="lnlinks" href="#hl-4-106">106</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-107"><a class="lnlinks" href="#hl-4-107">107</a></span><span class="cl">	<span class="k">return</span> <span class="nx">page</span><span class="p">.</span><span class="nf">NewPost</span><span class="p">(</span><span class="nx">text</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-108"><a class="lnlinks" href="#hl-4-108">108</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-109"><a class="lnlinks" href="#hl-4-109">109</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-110"><a class="lnlinks" href="#hl-4-110">110</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Microblog</span><span class="p">)</span> <span class="nf">GetPage</span><span class="p">(</span><span class="nx">author</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">Page</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-111"><a class="lnlinks" href="#hl-4-111">111</a></span><span class="cl">	<span class="nx">silo</span><span class="p">,</span> <span class="nx">found</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Pages</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">author</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-112"><a class="lnlinks" href="#hl-4-112">112</a></span><span class="cl">	<span class="k">if</span> <span class="p">!</span><span class="nx">found</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-113"><a class="lnlinks" href="#hl-4-113">113</a></span><span class="cl">		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">ErrNotFound</span>
</span></span><span class="line"><span class="ln" id="hl-4-114"><a class="lnlinks" href="#hl-4-114">114</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-115"><a class="lnlinks" href="#hl-4-115">115</a></span><span class="cl">	<span class="k">return</span> <span class="nx">silo</span><span class="p">.(</span><span class="o">*</span><span class="nx">Page</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="ln" id="hl-4-116"><a class="lnlinks" href="#hl-4-116">116</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-117"><a class="lnlinks" href="#hl-4-117">117</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-118"><a class="lnlinks" href="#hl-4-118">118</a></span><span class="cl"><span class="kd">type</span> <span class="nx">Page</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-119"><a class="lnlinks" href="#hl-4-119">119</a></span><span class="cl">	<span class="nx">ID</span>         <span class="kt">int</span>
</span></span><span class="line"><span class="ln" id="hl-4-120"><a class="lnlinks" href="#hl-4-120">120</a></span><span class="cl">	<span class="nx">Author</span>     <span class="nx">std</span><span class="p">.</span><span class="nx">Address</span>
</span></span><span class="line"><span class="ln" id="hl-4-121"><a class="lnlinks" href="#hl-4-121">121</a></span><span class="cl">	<span class="nx">CreatedAt</span>  <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span>
</span></span><span class="line"><span class="ln" id="hl-4-122"><a class="lnlinks" href="#hl-4-122">122</a></span><span class="cl">	<span class="nx">LastPosted</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span>
</span></span><span class="line"><span class="ln" id="hl-4-123"><a class="lnlinks" href="#hl-4-123">123</a></span><span class="cl">	<span class="nx">Posts</span>      <span class="nx">avl</span><span class="p">.</span><span class="nx">Tree</span> <span class="c1">// time -&gt; Post
</span></span></span><span class="line"><span class="ln" id="hl-4-124"><a class="lnlinks" href="#hl-4-124">124</a></span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-125"><a class="lnlinks" href="#hl-4-125">125</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-126"><a class="lnlinks" href="#hl-4-126">126</a></span><span class="cl"><span class="c1">// byLastPosted implements sort.Interface for []Page based on
</span></span></span><span class="line"><span class="ln" id="hl-4-127"><a class="lnlinks" href="#hl-4-127">127</a></span><span class="cl"><span class="c1">// the LastPosted field.
</span></span></span><span class="line"><span class="ln" id="hl-4-128"><a class="lnlinks" href="#hl-4-128">128</a></span><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">byLastPosted</span> <span class="p">[]</span><span class="o">*</span><span class="nx">Page</span>
</span></span><span class="line"><span class="ln" id="hl-4-129"><a class="lnlinks" href="#hl-4-129">129</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-130"><a class="lnlinks" href="#hl-4-130">130</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="nx">byLastPosted</span><span class="p">)</span> <span class="nf">Len</span><span class="p">()</span> <span class="kt">int</span>           <span class="p">{</span> <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-131"><a class="lnlinks" href="#hl-4-131">131</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="nx">byLastPosted</span><span class="p">)</span> <span class="nf">Swap</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">j</span> <span class="kt">int</span><span class="p">)</span>      <span class="p">{</span> <span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">a</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="p">=</span> <span class="nx">a</span><span class="p">[</span><span class="nx">j</span><span class="p">],</span> <span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-132"><a class="lnlinks" href="#hl-4-132">132</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="nx">byLastPosted</span><span class="p">)</span> <span class="nf">Less</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">j</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">LastPosted</span><span class="p">.</span><span class="nf">After</span><span class="p">(</span><span class="nx">a</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">LastPosted</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-133"><a class="lnlinks" href="#hl-4-133">133</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-134"><a class="lnlinks" href="#hl-4-134">134</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-135"><a class="lnlinks" href="#hl-4-135">135</a></span><span class="cl">	<span class="nx">o</span> <span class="o">:=</span> <span class="s">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-136"><a class="lnlinks" href="#hl-4-136">136</a></span><span class="cl">	<span class="k">if</span> <span class="nx">u</span> <span class="o">:=</span> <span class="nx">users</span><span class="p">.</span><span class="nf">GetUserByAddress</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">Author</span><span class="p">);</span> <span class="nx">u</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-137"><a class="lnlinks" href="#hl-4-137">137</a></span><span class="cl">		<span class="nx">o</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;# [%s](/r/demo/users:%s)\n\n&#34;</span><span class="p">,</span> <span class="nx">u</span><span class="p">.</span><span class="nf">Name</span><span class="p">(),</span> <span class="nx">u</span><span class="p">.</span><span class="nf">Name</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-138"><a class="lnlinks" href="#hl-4-138">138</a></span><span class="cl">		<span class="nx">o</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s\n\n&#34;</span><span class="p">,</span> <span class="nx">u</span><span class="p">.</span><span class="nf">Profile</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-139"><a class="lnlinks" href="#hl-4-139">139</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-140"><a class="lnlinks" href="#hl-4-140">140</a></span><span class="cl">	<span class="nx">o</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;## [%s](/r/demo/microblog:%s)\n\n&#34;</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Author</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Author</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-141"><a class="lnlinks" href="#hl-4-141">141</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-142"><a class="lnlinks" href="#hl-4-142">142</a></span><span class="cl">	<span class="nx">o</span> <span class="o">+=</span> <span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;joined %s, last updated %s\n\n&#34;</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">CreatedAt</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="s">&#34;2006-02-01&#34;</span><span class="p">),</span> <span class="nx">p</span><span class="p">.</span><span class="nx">LastPosted</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="s">&#34;2006-02-01&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-143"><a class="lnlinks" href="#hl-4-143">143</a></span><span class="cl">	<span class="nx">o</span> <span class="o">+=</span> <span class="s">&#34;## feed\n\n&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-144"><a class="lnlinks" href="#hl-4-144">144</a></span><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">u</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">p</span><span class="p">.</span><span class="nf">GetPosts</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-145"><a class="lnlinks" href="#hl-4-145">145</a></span><span class="cl">		<span class="nx">o</span> <span class="o">+=</span> <span class="nx">u</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span> <span class="o">+</span> <span class="s">&#34;\n\n&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-146"><a class="lnlinks" href="#hl-4-146">146</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-147"><a class="lnlinks" href="#hl-4-147">147</a></span><span class="cl">	<span class="k">return</span> <span class="nx">o</span>
</span></span><span class="line"><span class="ln" id="hl-4-148"><a class="lnlinks" href="#hl-4-148">148</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-149"><a class="lnlinks" href="#hl-4-149">149</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-150"><a class="lnlinks" href="#hl-4-150">150</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">NewPost</span><span class="p">(</span><span class="nx">text</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-151"><a class="lnlinks" href="#hl-4-151">151</a></span><span class="cl">	<span class="nx">now</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-152"><a class="lnlinks" href="#hl-4-152">152</a></span><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">LastPosted</span> <span class="p">=</span> <span class="nx">now</span>
</span></span><span class="line"><span class="ln" id="hl-4-153"><a class="lnlinks" href="#hl-4-153">153</a></span><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">Posts</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="nx">ufmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s%d&#34;</span><span class="p">,</span> <span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">),</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Posts</span><span class="p">.</span><span class="nf">Size</span><span class="p">()),</span> <span class="o">&amp;</span><span class="nx">Post</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-154"><a class="lnlinks" href="#hl-4-154">154</a></span><span class="cl">		<span class="nx">ID</span><span class="p">:</span>        <span class="nx">p</span><span class="p">.</span><span class="nx">Posts</span><span class="p">.</span><span class="nf">Size</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln" id="hl-4-155"><a class="lnlinks" href="#hl-4-155">155</a></span><span class="cl">		<span class="nx">Text</span><span class="p">:</span>      <span class="nx">text</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-156"><a class="lnlinks" href="#hl-4-156">156</a></span><span class="cl">		<span class="nx">CreatedAt</span><span class="p">:</span> <span class="nx">now</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-157"><a class="lnlinks" href="#hl-4-157">157</a></span><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-4-158"><a class="lnlinks" href="#hl-4-158">158</a></span><span class="cl">	<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="ln" id="hl-4-159"><a class="lnlinks" href="#hl-4-159">159</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-160"><a class="lnlinks" href="#hl-4-160">160</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-161"><a class="lnlinks" href="#hl-4-161">161</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">GetPosts</span><span class="p">()</span> <span class="p">[]</span><span class="o">*</span><span class="nx">Post</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-162"><a class="lnlinks" href="#hl-4-162">162</a></span><span class="cl">	<span class="nx">posts</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="o">*</span><span class="nx">Post</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Posts</span><span class="p">.</span><span class="nf">Size</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-163"><a class="lnlinks" href="#hl-4-163">163</a></span><span class="cl">	<span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-4-164"><a class="lnlinks" href="#hl-4-164">164</a></span><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">Posts</span><span class="p">.</span><span class="nf">ReverseIterate</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">key</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">value</span> <span class="kd">interface</span><span class="p">{})</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-165"><a class="lnlinks" href="#hl-4-165">165</a></span><span class="cl">		<span class="nx">postParsed</span> <span class="o">:=</span> <span class="nx">value</span><span class="p">.(</span><span class="o">*</span><span class="nx">Post</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-166"><a class="lnlinks" href="#hl-4-166">166</a></span><span class="cl">		<span class="nx">posts</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">postParsed</span>
</span></span><span class="line"><span class="ln" id="hl-4-167"><a class="lnlinks" href="#hl-4-167">167</a></span><span class="cl">		<span class="nx">i</span><span class="o">++</span>
</span></span><span class="line"><span class="ln" id="hl-4-168"><a class="lnlinks" href="#hl-4-168">168</a></span><span class="cl">		<span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="ln" id="hl-4-169"><a class="lnlinks" href="#hl-4-169">169</a></span><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-4-170"><a class="lnlinks" href="#hl-4-170">170</a></span><span class="cl">	<span class="k">return</span> <span class="nx">posts</span>
</span></span><span class="line"><span class="ln" id="hl-4-171"><a class="lnlinks" href="#hl-4-171">171</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-172"><a class="lnlinks" href="#hl-4-172">172</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-173"><a class="lnlinks" href="#hl-4-173">173</a></span><span class="cl"><span class="c1">// Post lists the specific update
</span></span></span><span class="line"><span class="ln" id="hl-4-174"><a class="lnlinks" href="#hl-4-174">174</a></span><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Post</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-175"><a class="lnlinks" href="#hl-4-175">175</a></span><span class="cl">	<span class="nx">ID</span>        <span class="kt">int</span>
</span></span><span class="line"><span class="ln" id="hl-4-176"><a class="lnlinks" href="#hl-4-176">176</a></span><span class="cl">	<span class="nx">CreatedAt</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span>
</span></span><span class="line"><span class="ln" id="hl-4-177"><a class="lnlinks" href="#hl-4-177">177</a></span><span class="cl">	<span class="nx">Text</span>      <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-4-178"><a class="lnlinks" href="#hl-4-178">178</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-179"><a class="lnlinks" href="#hl-4-179">179</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-180"><a class="lnlinks" href="#hl-4-180">180</a></span><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Post</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-181"><a class="lnlinks" href="#hl-4-181">181</a></span><span class="cl">	<span class="k">return</span> <span class="s">&#34;&gt; &#34;</span> <span class="o">+</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">ReplaceAll</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">Text</span><span class="p">,</span> <span class="s">&#34;\n&#34;</span><span class="p">,</span> <span class="s">&#34;\n&gt;\n&gt;&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#34;\n&gt;\n&gt; *&#34;</span> <span class="o">+</span> <span class="nx">p</span><span class="p">.</span><span class="nx">CreatedAt</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC1123</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#34;*&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-182"><a class="lnlinks" href="#hl-4-182">182</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Its a bit more code, but having a full-featured microblog with users and posts and persistent data, with only a Gno standard library is pretty amazing to me.</p>
<p>There is a lot more information about this project that you can dive into <a href="https://github.com/gnolang/workshops/blob/main/presentations/2023-06-26--go-to-gno--schollz/slides.pdf" target="_blank" >here</a> where you can also read aobut how to spin up your own server.</p>
<h2 id="more-about-gno">more about gno</h2>
<p>Gno provides Go developers with a smooth transition into the world of smart contracts. With a syntax close to Go and a platform like Gno.land, developing decentralized applications becomes accessible. While Gno introduces some unique features and limitations, its potential for web3 development is promising.</p>
<ul>
<li><strong>Examples galore:</strong> continue exploring with <a href="https://github.com/gnolang/gno/tree/master/examples" target="_blank" >the dozens of examples of Gno</a>.</li>
<li><strong>Getting started:</strong> more information on <a href="https://github.com/gnolang/gno/blob/master/examples/gno.land/r/demo/boards/README.md" target="_blank" >getting started with Gno</a>.</li>
<li><strong>Community creations:</strong> checkout what <a href="https://github.com/gnolang/awesome-gno" target="_blank" >people are building in gno</a>.</li>
<li><strong>Knoweldge hub:</strong> read previous <a href="https://github.com/gnolang/workshops" target="_blank" >talks about Gno</a>.</li>
<li><strong>Enter the conversation:</strong> join <a href="https://discord.com/invite/YFtMjWwUN7" target="_blank" >the discord</a>.</li>
</ul>
]]></description>
    </item>
    
    <item>
	    <title>habitus</title>
	    <image>https://schollz.com/img/habitus.jpg</image>
      <link>/tinker/habitus/</link>
      <pubDate>Mon, 26 Jun 2023 08:00:46 -0700</pubDate>
      
      <guid>/tinker/habitus/</guid>
      <description><![CDATA[<p>habitus is a two-day investigation of coding and field recording with the <a href="https://monome.org/docs/norns/" target="_blank" >monome norns sound computer</a>. It is a collective gathering to explore new habits of programming and musical practice through experimentation, research, collaborative learning, and the exercise of daydreams.</p>
<p>The workshop was designed for people with <em>all varieties of background and any amount of musical and programming experience</em>. These Europe workshops will be taught by a combination of teachers, including myself as well as the brilliant musician <a href="https://www.instagram.com/duellingants/" target="_blank" >Hans Teigar</a>, along with the unbounded enthusastic creators of this workshop - <a href="https://dndrks.com/" target="_blank" >Dan Derks</a> and <a href="https://github.com/jaseknighter" target="_blank" >Jonathan Snyder</a>.</p>
<p>The workshops are an incredible way to immerse yourself in an experience that aims to guide your own creativity to fulfill your musical/creative dreams, whatever they may be.</p>
<p>Personally I find myself <a href="/tags/norns/" >constantly making norns scripts</a> to deliver on some musical idea/dream. As one of the habitus guides this summer I will strive to relay some things I&rsquo;ve learned or found helpful.</p>
<p>Currently there are no upcoming workshops, but stay tuned!</p>
<h2 id="past-events">past events</h2>
<p>in the past, habitus operated in these spacetimes:</p>
<ul>
<li>August 12 + 13, 2023 in Stockholm, Sweden (<a href="https://llllllll.co/t/stockholm-norns-habitus-workshop-august-12-13-2023/62917" target="_blank" >link to discussion</a>)</li>
<li>August 5 + 6, 2023 in Berlin, Germany (<a href="https://llllllll.co/t/berlin-norns-habitus-workshop-august-3-4-5-6-2023/62286" target="_blank" >link to discussion</a>)</li>
<li>August 3 + 4, 2023 in Berlin, Germany (<a href="https://docs.google.com/forms/d/e/1FAIpQLSdok7xczg_JrWH6wPHdLVjQDLqkUQ5A2fy1hOLkNYrq9NMprA/viewform" target="_blank" >link to signup</a>)</li>
<li>September 24 + 25, 2022 @ Luck Dragon in Delhi, New York (<a href="https://luckdragon.space/event/220924-jonathan-snyder-dan-derks.html" target="_blank" >link</a>)</li>
<li>April 1 + 2, 2023 @ CETI in Portland, Oregon (<a href="https://www.pdx.edu/art-design/events/ceti-sound-spa-workshops" target="_blank" >link</a>), with the incredible <a href="http://franciscobotello.com/" target="_blank" >Francisco Botello</a></li>
</ul>
]]></description>
    </item>
    
    <item>
	    <title>Ube</title>
	    <image>https://schollz.com/img/scube.png</image>
      <link>/tinker/ube/</link>
      <pubDate>Sun, 26 Feb 2023 08:00:46 -0700</pubDate>
      
      <guid>/tinker/ube/</guid>
      <description><![CDATA[<p>Ube is a Unpredictable Binaural Experimentatal granular synthesizer. It uses SuperCollider code to create a macrogranular sampler that reads audio from a buffer. It uses various LFOs and envelopes to modulate parameters such as playback position, volume, and panning and creates a rich and constantly evolving texture. The sound is then balanced between left and right channels using another LFO for panning, and an envelope generator controls the amplitude.</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/802698414" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<p>This SuperCollider code also creates a graphics display of a waveform area, a playhead, and a pan symbol. The waveform area (large rectangle on each row) is drawn using a color that is dynamically adjusted based on the volume level of the audio being played. The playhead (white line) indicates the current position of the playback, and the pan symbol (small rectangle) indicates the panning position. The resulting display provides a visual representation of the audio playback and allows for visual feedback during interactive performance.</p>
<h3 id="usage--installation">Usage / Installation</h3>
<ol>
<li>Go to the <a href="https://supercollider.github.io/download" target="_blank" >SuperCollider website</a> to download and run the latest SuperCollider installer.</li>
<li>Download <a href="https://github.com/supercollider/sc3-plugins/releases/tag/Version-3.13.0" target="_blank" >the latest <strong>sc3-plugins</strong></a> and install them into <a href="https://supercollider.github.io/sc3-plugins/#Installation" target="_blank" >the SuperCollider extensions folder</a>.</li>
<li>Download <a href="https://github.com/schollz/portedplugins/releases/tag/v0.4.5" target="_blank" >the latest <strong>portedplugins</strong></a> and install them into <a href="https://supercollider.github.io/sc3-plugins/#Installation" target="_blank" >the SuperCollider extensions folder</a>.</li>
<li>Download <a href="https://raw.githubusercontent.com/schollz/supercollisions/main/Ube.sc" target="_blank" ><strong>Ube.sc</strong></a> and also copy it into the <a href="https://supercollider.github.io/sc3-plugins/#Installation" target="_blank" >SuperCollider extensions folder</a>.</li>
<li>In the SuperCollider IDE you can run the following code to start it (make sure to change the file that is loaded):</li>
</ol>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
s.waitForBoot({
	var pairs;
	// load the ube class
	u=Ube.new(Server.default);

	// load a file
	u.loadTape(tape:1,filename:thisProcess.nowExecutingPath.dirname++&#34;/kalimba.wav&#34;);

	// play the tape with lots of players
	pairs=[
		[1, 0], // rate is the first number, db is the second
		[2, -6],
		[0.5, -6],
		[4, -9],
		[0.25, -9],
		[0.125,0],
	];
	pairs.do({ arg v,i;
		u.playTape(tape:1,player:i,rate:v[0],db:v[1],timescale:1.0);
	});

	// show gui
	u.gui;
});
)
</code></pre><p>Though I made Ube primarily as a teaching tool, its also been a fun exploratory musical instrument.</p>
<p>For more of my music, check out my <a href="https://infinitedigits.bandcamp.com" target="_blank" ><strong>Bandcamp</strong></a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>am in awe.</title>
	    <image>https://schollz.com</image>
      <link>/i/in-awe/</link>
      <pubDate>Tue, 17 Jan 2023 08:14:42 -0800</pubDate>
      
      <guid>/i/in-awe/</guid>
      <description><![CDATA[<p><img src="https://www.moma.org/media/W1siZiIsIjEwNTc4MyJdLFsicCIsImNvbnZlcnQiLCItcXVhbGl0eSA5MCAtcmVzaXplIDIwMDB4MTUwMFx1MDAzZSJdXQ.jpg?sha=b4b9abfdb804a3f9" alt="two stones"></p>
<blockquote>
<p>At a certain point, Celmins selected eleven stones, and set out to make an exact copy of each one. “They seemed so beautiful that I wanted to make them myself, I wanted to see how close I could come.” Celmins believes that, if you spend enough time on a work, something else might come into play.</p>
</blockquote>
<p>via <a href="https://web.archive.org/web/20200221043319/https://www.newyorker.com/magazine/2019/09/02/vija-celmins-surface-matters" target="_blank" >Vija Celmins</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>finished a painting.</title>
	    <image>https://schollz.com</image>
      <link>/i/220115_2/</link>
      <pubDate>Sun, 08 Jan 2023 08:20:49 -0800</pubDate>
      
      <guid>/i/220115_2/</guid>
      <description><![CDATA[<p><img src="/img/two_donkeys.jpg" alt="two donkeys."></p>
]]></description>
    </item>
    
    <item>
	    <title>Producing multichannel audio synthesis with norns</title>
	    <image>https://schollz.com/img/multichannel1.jpg</image>
      <link>/tinker/220102/</link>
      <pubDate>Mon, 02 Jan 2023 02:35:41 -0700</pubDate>
      
      <guid>/tinker/220102/</guid>
      <description><![CDATA[<p>I previously wrote <a href="/til/221231" >a short tutorial</a> for multichannel audio on a Linux computer with USB dongles and JACK. I recenty figured out how to easily do this with the <a href="https://monome.org/docs/norns/" target="_blank" >norns sound computer</a>.</p>
<p>To start, you just need USB audio adapters. It might be best to get ones that have a 48k sampling rate, as thats the norns sampling rate (others should work, but it will cost CPU to resample them). You can get these as cheap as <a href="https://www.amazon.com/Sabrent-External-Adapter-Windows-AU-MMSA/dp/B00IRVQ0F8/" target="_blank" >$8</a>.</p>
<p>After plugging in the adapters, the norns code needs to be slightly modified to utilize the extra audio channels. First add a file, <code>~/.asoundrc</code> that has bindings for each device. Here is my file for two dongles, for a total of six channels:</p>
<pre tabindex="0"><code>pcm.merge {
    type multi;
    slaves.a.pcm hw:sndrpimonome
    slaves.a.channels 2;
    slaves.b.pcm hw:1
    slaves.b.channels 2;
    slaves.c.pcm hw:2
    slaves.c.channels 2;
    bindings.0.slave a;
    bindings.0.channel 0;
    bindings.1.slave a;
    bindings.1.channel 1;
    bindings.2.slave b;
    bindings.2.channel 0;
    bindings.3.slave b;
    bindings.3.channel 1;
    bindings.4.slave c;
    bindings.4.channel 0;
    bindings.5.slave c;
    bindings.5.channel 1;
}

ctl.merge {
    type hw
    card 0
}
</code></pre><p>You can remove <code>slave c</code> if you only have a single dongle, or you can add more <code>slave X</code> if you have more than two dongles.</p>
<p>In this example I am using six channels, so you&rsquo;ll also need to specify this in the <code>jack</code> systemd file. You can edit this file with <code>sudo</code>:</p>
<pre tabindex="0"><code>&gt; sudo vim /etc/systemd/system/norns-jack.service
</code></pre><p>Find the line that starts with <code>ExecStart</code> and change it to:</p>
<pre tabindex="0"><code>ExecStart=/usr/bin/jackd -R -P 95 -d alsa -P merge -C hw:sndrpimonome -r 48000 -n 3 -p 1024 -S -s -o 6 -i 2
</code></pre><p>The <code>-o 6</code> specifies 6 channels. If you have more or less you can change this number.</p>
<p>Finally, we have to edit the <code>crone</code> service to have 6 channels (or however many you have). Edit the <code>Crone.sc</code> file:</p>
<pre tabindex="0"><code>&gt; vim ~/norns/sc/core/Crone.sc
</code></pre><p>And add the following line:</p>
<pre tabindex="0"><code>server.options.numOutputBusChannels = 6;
</code></pre><p>after the line that says <code>server.latency = 0.05;</code>. This will make sure that SuperCollider utilizes as many buses for output as you specify.</p>
<p>Now you can restart norns.</p>
<p>Finally, you have to connect <code>jack</code> to the outputs. To do this, you can simply run <code>jack_connect</code> with as many new ports added. For these examples, I have six channels - four are extra - so I can attach them using:</p>
<pre tabindex="0"><code>jack_connect system:playback_3 SuperCollider:out_3
jack_connect system:playback_4 SuperCollider:out_4
jack_connect system:playback_5 SuperCollider:out_5
jack_connect system:playback_6 SuperCollider:out_6
</code></pre><p>This will connect the new channels (3-6) to SuperCollider.</p>
<p>That&rsquo;s it! Now you can make SuperCollider synths that output through channels 1-6 using multiexpansion. For example, here&rsquo;s a diff that changes the <code>PolyPerc</code> engine to use six channels to output, instead of 2:</p>
<pre tabindex="0"><code class="language-git" data-lang="git">diff --git a/sc/engines/Engine_PolyPerc.sc b/sc/engines/Engine_PolyPerc.sc
index 88362aca..436de50f 100644
--- a/sc/engines/Engine_PolyPerc.sc
+++ b/sc/engines/Engine_PolyPerc.sc
@@ -17,10 +17,10 @@ Engine_PolyPerc : CroneEngine {
                pg = ParGroup.tail(context.xg);
     SynthDef(&#34;PolyPerc&#34;, {
                        arg out, freq = 440, pw=pw, amp=amp, cutoff=cutoff, gain=gain, release=release, pan=pan;
-                       var snd = Pulse.ar(freq, pw);
+                       var snd = Pulse.ar([freq,freq*2,freq/2,freq/4,freq*1.5,freq*3], pw);
                        var filt = MoogFF.ar(snd,cutoff,gain);
                        var env = Env.perc(level: amp, releaseTime: release).kr(2);
-                       Out.ar(out, Pan2.ar((filt*env), pan));
+                       Out.ar(0, (filt*env));
                }).add;
</code></pre><p>Each speaker output (or three sets of headphones) will now all have different synthesized audio!</p>
]]></description>
    </item>
    
    <item>
	    <title>stood on a frozen lake.</title>
	    <image>https://schollz.com</image>
      <link>/i/220115/</link>
      <pubDate>Sun, 01 Jan 2023 08:14:42 -0800</pubDate>
      
      <guid>/i/220115/</guid>
      <description><![CDATA[<p><img src="/img/frozen_lake.jpg" alt="frozen lake."></p>
<p>(that&rsquo;s not me).</p>
]]></description>
    </item>
    
    <item>
	    <title>Producing multichannel synthesis with Linux.</title>
	    <image>https://schollz.com/img/multichannel2.jpg</image>
      <link>/tinker/221231/</link>
      <pubDate>Sat, 31 Dec 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221231/</guid>
      <description><![CDATA[<p>SuperCollider has a <a href="https://depts.washington.edu/dxscdoc/Help/Guides/Multichannel-Expansion.html" target="_blank" >pretty incredible multichannel expansion</a> system. It&rsquo;s usually useful to use as a way to generate multiple channels that are later mixed down to one or two (for headphones), but they can be quickly used to more than two physical audio channels.</p>
<p>On Linux, using multiple audio channels is quick and easy. You can buy the cheap DACs that do &ldquo;USB audio&rdquo; (~$10) and plug them into your computer. Then setup a <code>~/.asoundrc</code> file like the following (more info about this <a href="https://jackaudio.org/faq/multiple_devices.html" target="_blank" >on the Jack audio page</a>):</p>
<pre tabindex="0"><code>pcm.merge {
    type multi;
    slaves.a.pcm hw:0
    slaves.a.channels 2;
    slaves.b.pcm hw:1
    slaves.b.channels 2;
    bindings.0.slave a;
    bindings.0.channel 0;
    bindings.1.slave b;
    bindings.1.channel 0;
    bindings.2.slave a;
    bindings.2.channel 1;
    bindings.3.slave b;
    bindings.3.channel 1;
}

ctl.merge {
    type hw
    card 0
}
</code></pre><p>This defines a new sound card &ldquo;merge&rdquo; that is the combination of the the first two audio devices (computer speakers and USB audio by default). You can easily add more <code>slaves</code> and more <code>bindings</code> depeneding on which cards are plugged in. The <code>hw:0/1</code> are based off the <code>cat /proc/asound/cards</code> output. Then you can simply run JACK using this merged soundcard (named &ldquo;merge&rdquo;):</p>
<pre tabindex="0"><code>&gt; jackd -v -R -P 95 -d alsa -P merge -C hw:0
</code></pre><p>(Note that the capture is still specified as <code>hw:0</code>).</p>
<p>Then in SuperCollider, you can open it and boot it with some lines specifying the number of output bus channels (in the example above we only have 4, but it could be many):</p>
<pre tabindex="0"><code>s=Server.local;
s.options.numOutputBusChannels=4;
s.boot;
</code></pre><p>Now you can use things like <code>PanAz</code> to pan sounds across all four channels, e.g.</p>
<pre tabindex="0"><code>(
{
	var snd;
	snd = PanAz.ar(4,SinOsc.ar(220)*0.1,MouseX.kr(-1,1),orientation:0);
	snd = snd + PanAz.ar(4,SinOsc.ar(110)*0.1,MouseX.kr(-1,1),orientation:0.25);
	snd = snd + PanAz.ar(4,SinOsc.ar(880)*0.1,MouseX.kr(-1,1),orientation:0.5);
	snd = snd + PanAz.ar(4,SinOsc.ar(442)*0.1,MouseX.kr(-1,1),orientation:0.75);
	Out.ar(0,snd.poll);
}.play;
)
</code></pre><p>More info about this in <a href="https://books.google.com/books?id=aE7LSAAACAAJ&amp;printsec=copyright#v=onepage&amp;q&amp;f=false" target="_blank" >The SuperCollider Book</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Gridding images for painting on the command-line</title>
	    <image>https://schollz.com/img/paintgrid.jpg</image>
      <link>/tinker/221224/</link>
      <pubDate>Sat, 24 Dec 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221224/</guid>
      <description><![CDATA[<p>I wanted to create a square grid on an image for painting.</p>
<p>First download <code>grid</code> from Fred&rsquo;s ImageMagick Scripts: <a href="http://www.fmwconcepts.com/imagemagick/downloadcounter.php?scriptname=grid&amp;dirname=grid" target="_blank" >link</a>.</p>
<p>It&rsquo;s helpful to resize to a easy number:</p>
<pre tabindex="0"><code>convert 1.jpg -resize 1600 2.jpg
</code></pre><p>Then you can make a grid with lines in one line:</p>
<pre tabindex="0"><code>./grid -c white -s 200 -t 2 2.jpg 3.jpg
</code></pre><p>That&rsquo;s it! Change <code>100</code> to <code>200</code> or <code>400</code> for bigger spacings.</p>
]]></description>
    </item>
    
    <item>
	    <title>My 2022 in code</title>
	    <image>https://schollz.com/img/norns.jpg</image>
      <link>/tinker/2022/</link>
      <pubDate>Fri, 23 Dec 2022 07:50:07 -0700</pubDate>
      
      <guid>/tinker/2022/</guid>
      <description><![CDATA[<p>I made <a href="https://github.com/search?q=user%3Aschollz&#43;created%3A%3E%3D2022&amp;type=Repositories&amp;ref=advsearch&amp;l=&amp;l=&amp;p=1" target="_blank" >66 repositories</a> in 2022 and made almost 4,000 commits. Most of my coding is for <a href="https://monome.org/docs/norns/" target="_blank" >norns</a> scripts, it seems I released around 14 norns scripts this year (though made many more).</p>
<p>I made <a href="https://github.com/schollz/moonraker" target="_blank" >moonraker</a>, a norns script to generate random syncopations from samples using sequences of euclidean rhythms.</p>
<p>I made <a href="https://github.com/schollz/lorenzos-drums" target="_blank" >lorenzo&rsquo;s drums</a>, a norns script for sequencing a meticulously sampled drumset.</p>
<p>I made a second version of <a href="https://github.com/schollz/mx.samples2" target="_blank" >mx.samples</a>, but found that it used slightly more CPU so I never officially released it.</p>
<p>I made a special version of mx.samples that only used marimba, called <a href="https://github.com/schollz/mallets" target="_blank" >mallets</a> but also never released it officially.</p>
<p>Speaking of unreleased scripts, there is also <a href="https://github.com/schollz/bb" target="_blank" >bb</a> (a bouncing ball norns script), <a href="https://github.com/schollz/qwertymidi" target="_blank" >qwertymidi</a> (a script for using a keyboard as a midi controller), io (a script that sequences using arpeggios), <a href="https://github.com/schollz/grdctl" target="_blank" >grdctl</a> (like 0-ctrl but for the grid), and <a href="https://github.com/schollz/softdelay" target="_blank" >softdelay</a> (a norns script mod for a simple softcut delay).</p>
<p>I made a norns mod to <a href="https://github.com/schollz/broadcast" target="_blank" >broadcast audio</a>. This later became an entire service called <a href="https://github.com/schollz/streammyaudio" target="_blank" >stream my audio</a> and is available on any PC.</p>
<p>I made <a href="https://github.com/schollz/acid-test" target="_blank" >acid-test</a>, a norns script that makes generative acid basslines using markov chains. This later became <a href="https://github.com/schollz/oomph" target="_blank" >oomph</a> which used a better 303 emulation that I wrote in SuperCollider.</p>
<p>Speaking of SuperCollider, I wrote <a href="https://github.com/schollz/ouroboros" target="_blank" >ouroboros</a>, a SuperCollider script for recording seamless loops.</p>
<p>Speaking of seamless loops, I wrote <a href="https://github.com/schollz/seamlessloop" target="_blank" >seamlessloops</a>, a small binary that makes loops out of any audio.</p>
<p>I made <a href="https://github.com/schollz/dnb.lua" target="_blank" >dnb.lua</a> which is a Lua script that makes generative music using sox. I made a norns script based of this called <a href="https://github.com/schollz/makebreakbeat" target="_blank" >makebreakbeat</a>. This later became <a href="https://github.com/schollz/sampswap" target="_blank" >sampswap</a>. This later resulted in an album based on seamless loops using a &ldquo;random audio workstation&rdquo;, the album is <a href="https://infinitedigits.bandcamp.com/album/paracosms" target="_blank" >on bandcamp</a> and all <a href="https://github.com/schollz/raw" target="_blank" >the code</a> is open-source. There is a norns script based around all this code called <a href="https://github.com/schollz/paracosms" target="_blank" >paracosms</a> which I also released.</p>
<p>I made a library to convert SVG images to gcode for penplotters, <a href="https://github.com/schollz/svg2gcode" target="_blank" >svg2gcode</a>.</p>
<p>I made a two-channel tuner for norns called <a href="https://github.com/schollz/2tuner" target="_blank" >2tuner</a> which eventually got integrated into <a href="https://github.com/schollz/zxcvbn" target="_blank" >zxcvbn</a>, another script that I wrote that compiles many things I&rsquo;ve learned over the years into a near-complete DSP scaffold.</p>
<p>I scavenged code across the internet to make a easy-to-use <a href="https://github.com/schollz/norns-desktop" target="_blank" >norns that runs on a desktop</a>, which can also be used online (currently online at <a href="https://play.nornns.online" target="_blank" >play.norns.online</a>).</p>
<p>I cobbled over some SuperCollider code for tape emulation into a norns script called <a href="https://github.com/schollz/tapedeck" target="_blank" >tapedeck</a>.</p>
<p>I recently made a little script that uses tapedeck with amen breaks, called <a href="https://github.com/schollz/amenbreak" target="_blank" >amenbreak</a>.</p>
<p>I made a relaxing norns script called <a href="https://github.com/schollz/l_ll__l_" target="_blank" >l ll l</a> which is like a musical emission spectrum.</p>
<p>I have more musical ideas to explore in 2023. Looking forward to it.</p>
]]></description>
    </item>
    
    <item>
	    <title>Setting up wireguard&#43;pihole with Ubuntu and other devices</title>
	    <image>https://schollz.com/img/pihole-h.jpg</image>
      <link>/tinker/221220/</link>
      <pubDate>Tue, 20 Dec 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221220/</guid>
      <description><![CDATA[<ol>
<li>Get a $4/month Droplet on DigitalOcean with the Pihole+VPN (in the Marketplace). This automatically sets up Pihole with Wireguard support.</li>
<li>Login to the droplet and run <code>./regen-vpn-keys.sh X</code> where <code>X</code> is the number of keys you want to generate. This will generate QR codes that you can load into your phone/iPad via the wireguard app.</li>
<li>Phone/iPad skips to step 4. Get it working on Ubuntu. Copy one of the interfaces to <code>/etc/wireguard/wg0.conf</code>. The full thing looks like:</li>
</ol>
<pre tabindex="0"><code>[Interface]
Address = ...
DNS = ...
PrivateKey = ...

[Peer]
Endpoint = ...
PersistentKeepalive = ...
PublicKey = ...
PresharedKey = ...
AllowedIPs = ...
</code></pre><p>Now install wireguard:</p>
<pre tabindex="0"><code>sudo apt update
sudo apt install wireguard
</code></pre><p>Now, depending on the system you need <a href="https://superuser.com/questions/1500691/usr-bin-wg-quick-line-31-resolvconf-command-not-found-wireguard-debian" target="_blank" >to fix resolveconf</a>:</p>
<pre tabindex="0"><code>sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf
</code></pre><ol start="4">
<li>Now start wireguard! On the phone you just click a button. In Ubuntu you do:</li>
</ol>
<pre tabindex="0"><code>sudo wg-quick up wg0
</code></pre><p>Get stats with</p>
<pre tabindex="0"><code>sudo wg show wg0
</code></pre><p>Turn it off with</p>
<pre tabindex="0"><code>sudo wg-quick down wg0
</code></pre><ol start="5">
<li>Enjoy ad-less browsing with a VPN.</li>
</ol>
]]></description>
    </item>
    
    <item>
	    <title>Calculating the CPU usage of any process in Linux</title>
	    <image>https://schollz.com/img/proccpu.jpg</image>
      <link>/tinker/221202/</link>
      <pubDate>Fri, 02 Dec 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221202/</guid>
      <description><![CDATA[<p>I wanted to easily calculate CPU usage of specific processes at given intervals. Turns out this is very easy to do.</p>
<p>Simply collect the stat information:</p>
<pre tabindex="0"><code>cat /proc/&lt;pid&gt;/stat
</code></pre><p>Add the 14th and 15th columns together and that&rsquo;s the total clock ticks used by that process.</p>
<p>Now wait a specified duration (say 2 seconds) and collect that information again. Take the difference between total clock ticks between the two times, and then divide by the duration that elapsed. This is the total clock ticks used by that process during that time.</p>
<p>To convert the total clock ticks to time, you&rsquo;ll need to divide clock ticks per second which you can get with a simple command:</p>
<pre tabindex="0"><code>getconf CLK_TCK
</code></pre><p>Here&rsquo;s a simple Go implementation I made: <a href="https://github.com/schollz/zxcvbn/blob/7b6a79215fdc0da159908245f9b899aded26c0b6/lib/osccpu/main.go#LL59;L90" target="_blank" >example in Go</a> and another example I found online for one written in bash: <a href="https://www.baeldung.com/linux/total-process-cpu-usage" target="_blank" >example in bash</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>How the free energy principle may imply a feedback mechanism for learning</title>
	    <image>https://schollz.com/img/freeenergy.png</image>
      <link>/tinker/221201/</link>
      <pubDate>Thu, 01 Dec 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221201/</guid>
      <description><![CDATA[<p>An interesting article emerged recently (<em>Kagan, Brett J., et al. &ldquo;In vitro neurons learn and exhibit sentience when embodied in a simulated game-world.&rdquo; Neuron (2022).</em>) that teaches brain cells to play pong, but at the same time tests an interesting hypothesis about utilizing the free enrgy principle as a feedback learning mechanism. From the paper:</p>
<blockquote>
<p>One proposition for how intelligent behavior may arise in an intelligent system embodied in an environment is the theory of active inference via the free energy principle (FEP) (Friston et al., 2012). The FEP suggests a testable implication that at every spatiotemporal scale, any self-organizing system separate from its environment seeks to minimize its variational free energy (VFE)</p>
</blockquote>
<p>The testing from the paper:</p>
<blockquote>
<p>To test whether learning reflects a reduction in VFE within BNNs, we used the information entropy of neuronal responses as a proxy for the average surprise (a.k.a. self-information), which is upper-bounded by VFE (see STAR Methods). We predicted a reduction in information entropy during the learning of gameplay. We further predicted an increase in entropy following unpredictable (random) feedback, reflecting and ensuing state of “surprise” (and, implicitly, high VFE), relative to pre-feedback states.</p>
</blockquote>
]]></description>
    </item>
    
    <item>
	    <title>Tinting an image with `imagemagick`</title>
	    <image>https://schollz.com/img/imagemagick.svg</image>
      <link>/tinker/221130/</link>
      <pubDate>Wed, 30 Nov 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221130/</guid>
      <description><![CDATA[<p>If you have an image that needs to be tinted, you can easily do this with <code>imagemagick</code>. First install <code>imagemagick</code>:</p>
<pre tabindex="0"><code>&gt; sudo apt-get install imagemagick
</code></pre><p>Then run this command to convert it:</p>
<pre tabindex="0"><code>&gt; convert input.png -colorspace gray -fill green -tint 50 tinted.png
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Generating automatically tuned drums for OP-Z and OP-1</title>
	    <image>https://schollz.com/img/drumtune.jpg</image>
      <link>/tinker/221116/</link>
      <pubDate>Tue, 15 Nov 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221116/</guid>
      <description><![CDATA[<p>I recently came upon <a href="https://llllllll.co/t/soma-labs-pulsar-23/14447/339" target="_blank" >these amazing drum samples</a> recorded by @postsolarpunk on a Pulsar-23. I was interested in utilizing these for the OP-Z and OP-1. I already <a href="/blog/op1" >reverse engineered OP-1/OP-Z patches</a> but I wanted to take things a step further and automatically tune all the drum hits to a specific key.</p>
<p>All the code is on my github: <a href="https://github.com/schollz/opkit" target="_blank" >schollz/opkit</a>.</p>
<p>But basically, all I did was first trim off silence from the end of each sample using sox:</p>
<pre tabindex="0"><code>sox sample.wave new_sample.wave reverse silence 1 0.1 -50d reverse
</code></pre><p>That command will trim any silence over 100 ms at the end of the file, where silence is defined by anything at or below -50 dB.</p>
<p>Then I determined the pitch of each hit using aubio to calculate the most fundamental pitch:</p>
<pre tabindex="0"><code>aubio pitch -i new_sample.wav -m schmitt -u midi
</code></pre><p>That results in a list, and I used the mode to find the most common rounded midi note and then took the overage of +/-2 notes away from the mode. This worked out to be accurate in demo sounds using the &ldquo;mcomb&rdquo; or &ldquo;schmitt&rdquo; algorithms.</p>
<p>After that I just wrote some Golang glue that selected random files, re-pitched them to whatever note I want (say &ldquo;C&rdquo;), and then randomly included up to 11.5 seconds (that&rsquo;s the OP limit on the sampler) and used <a href="https://github.com/schollz/teoperator" target="_blank" >schollz/teoperator</a> to splice them together.</p>
<p>Here&rsquo;s the result of a patch made from splicing kick sounds in the key of C:</p>
<p><audio src="/img/kick_0_c.mp3" class="waveform"></audio></p>
<p>The patch has extra information in the header file that tells the OP-Z and OP-1 how to calculate the sample start/end points.</p>
<p>And here&rsquo;s a little example track I made with kick, snare, hat and bass sounds from the Pulsar-23, all pitched to &ldquo;C&rdquo;. Made on an OP-Z:</p>
<p><audio src="/img/pulsar-23.mp3" class="waveform"></audio></p>
<p>In the <a href="https://github.com/schollz/opkit" target="_blank" >schollz/opkit</a> there are instructions for generating your own kits, but I generated a bunch of C-based kits if you want to just try: <a href="https://schollz.com/kits/c" target="_blank" >Pulsar-23 OP-1/OP-Z kits</a>.</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>Faking ambisonics with SuperCollider</title>
	    <image>https://schollz.com/img/ambisonics.png</image>
      <link>/tinker/221106/</link>
      <pubDate>Sun, 06 Nov 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221106/</guid>
      <description><![CDATA[<p>SuperCollider has a <a href="http://doc.sccode.org/Guides/Intro-to-the-ATK.html" target="_blank" >pretty incredible Ambisonic Toolkit</a>. However whenever I use it it gets very complicated, uses a bit too much CPU, and the result is not exactly what I hoped. The result I&rsquo;m looking for is simply to project a stereo audio signal around the head so that it can appear to be randomly around you - to the left, to the right, and also behind and in front.</p>
<p>Turns out this can be done in a imprecise and coarse way that does have some semblance to random binaural positioning, by using slight delays between the two channels and some filtering. Here&rsquo;s a small example:</p>
<pre tabindex="0"><code>(
b = Buffer.read(s, Platform.resourceDir +/+ &#34;sounds/a11wlk01.wav&#34;);
{
	var snd=PlayBuf.ar(1,b,loop:1);
	var pan=SinOsc.kr(1/7.13,Rand(0,6))/1.5;
	var pan2=SinOsc.kr(1/12.123,Rand(0,6))/1.5;
	var pan3=SinOsc.kr(1/15.12354,Rand(0,6))/1.5;
	var amp=SinOsc.kr(1/17.123,Rand(0,6)).range(0.25,0.75);
	snd=Pan2.ar(snd,0);
	//snd=WhiteNoise.ar(0.1)!2;
	snd=[snd[0],snd[1]];
	snd=[
		LPF.ar(snd[0],LinExp.kr((pan2&lt;0)*pan2.abs,0,1,4000,18000).poll),
		LPF.ar(snd[1],LinExp.kr((pan2&gt;0)*pan2.abs,0,1,4000,18000).poll)
	];
	snd[0]=SelectX.ar(((pan&gt;0)*pan.abs),[snd[0],DelayN.ar(snd[0],0.04,0.04)]);
	snd[1]=SelectX.ar(((pan&lt;0)*pan.abs),[snd[1],DelayN.ar(snd[1],0.04,0.04)]);
	snd=Balance2.ar(snd[0],snd[1],pan3);
	Out.ar(0,snd*amp);
}.play;
)
</code></pre><p>In this example the channels are split and then each channel undergoes some random oscilation in its low-pass filter and the side of its small delay (40 ms). There is also a traditional equal-power pan at the end. Its certainly head-swirling, although not quite at the level of true ambisonics.</p>
]]></description>
    </item>
    
    <item>
	    <title>Emulating a JP-8000 supersaw in SuperCollider</title>
	    <image>https://schollz.com/img/jp8000.png</image>
      <link>/tinker/221103/</link>
      <pubDate>Thu, 03 Nov 2022 06:35:41 -0700</pubDate>
      
      <guid>/tinker/221103/</guid>
      <description><![CDATA[<p>I recently learned about this great <a href="https://web.archive.org/web/20110627045129/https://www.nada.kth.se/utbildning/grukth/exjobb/rapportlistor/2010/rapporter10/szabo_adam_10131.pdf" target="_blank" >thesis by Adam Szabo</a> where they carefully reverse engineer the detuning curve for the oscillators from a JP-8000 synthesizer. Essentially they evaluated frequency space for a bunch of values set on the synthesizer and calculated the space between the different frequencies from the fundamental.</p>
<p><img src="/img/detuning.png" alt="detuning"></p>
<p>This resulted in a fitting equation to get the same cruve, and luckily enough there is already <a href="https://gist.github.com/audionerd/fe50790b7601cba65ddd855caffb05ad" target="_blank" >a SuperCollider port of this equation from Eric Skogen</a>. For my ears, it sounds great.</p>
<p>This is a great example of how a simple idea can become an amazing idea in its implementation. Detuning six oscillators is a simple thing, but the non-linear response built-in to the JP-8000 makes it way more pleasant as it increases the &ldquo;sweet&rdquo; spots.</p>
]]></description>
    </item>
    
    <item>
	    <title>Running norns without the shield</title>
	    <image>https://schollz.com/img/norns.png</image>
      <link>/tinker/norns/</link>
      <pubDate>Fri, 07 Oct 2022 08:00:46 -0700</pubDate>
      
      <guid>/tinker/norns/</guid>
      <description><![CDATA[<p>norns is a sound computer released by <a href="https://monome.org/" target="_blank" >monome.org</a> that lets you use lua to write simple scripts that interface with SuperCollider, softcut, and cairo-drawing utilities. Here are <a href="https://schollz.com/tags/norns/" target="_blank" >some of the things I&rsquo;ve made</a> with norns.</p>
<p>Normally norns comes packaged as a <a href="https://vimeo.com/267112253" target="_blank" >standalone device</a> or as a <a href="https://github.com/monome/norns-shield" target="_blank" >shield for a Raspberry Pi</a>. However it is possible to utilize it without a physical hardware, and just run on a regular headless Raspberry Pi without the shield.</p>
<h2 id="expectations">Expectations</h2>
<p>Running norns on a headless pi without the shield misses out on several things without the physical device. Keep this in mind:</p>
<p>The physical device uses buttons and encoders and a screen. The system described below uses a web browser as a screen and uses your mouse to interact with the virtual buttons/encoders, which pales in comparison to the physical implementation (but works nonetheless).</p>
<p>The physical norns device easily interfaces with midi gear. The system described below does not (although in theory it could with Web Midi but I&rsquo;m not interested in implementing that).</p>
<p>The physical norns device has low latency and high-quality audio. Depending on the USB audio device you get you would also have low latency and high-quality (24-bit, low noise) but it will depend on your device.</p>
<h1 id="installation-on-a-docker-enabled-computer">Installation on a Docker-enabled computer</h1>
<p>Currently these instructions <em>only</em> work if you have an <code>amd64</code> processor. If you have a Raspberry Pi, you&rsquo;ll need to skip to the next instructions.</p>
<p>If you want to install this on a computer that runs Docker (Linux server or something), then the instructions are much simpler. You can simply clone the main directory and build the docker image:</p>
<pre tabindex="0"><code>git clone https://github.com/schollz/norns-desktop
./run_docker.sh
</code></pre><p>This will automatically start norns with an internet radio as well. If you want to use on-board audio you need to edit <code>jackdr</code> from the following:</p>
<pre tabindex="0"><code>/usr/bin/jackd -R -P 95 -d dummy -r 48000 -p 128
</code></pre><p>to something that uses your audio (e.g. audio device 0):</p>
<pre tabindex="0"><code>/usr/bin/jackd -R -P 95 -d alsa -d hw:0 
</code></pre><p>And then run <code>run_docker.sh</code> again.</p>
<p>Open <code>&lt;your pi IP address&gt;:8889</code> to see your norns and interact with it. Open <code>&lt;your pi IP address&gt;:5000</code> to use maiden to install scripts and edit things and restart the system.</p>
<h1 id="installation-on-a-raspberry-pi-full-instructions">Installation on a Raspberry Pi (full instructions)</h1>
<h2 id="pre-requisites">Pre-requisites</h2>
<ul>
<li>Raspberry Pi <a href="https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit" target="_blank" ><em>with 64-bit OS installed</em></a></li>
<li>2x2 USB interface (<a href="https://www.amazon.com/dp/B0023BYDHK?psc=1&amp;linkCode=ll1&amp;tag=scholl-20&amp;linkId=f0cac0eea4bc97d5eb45ba858012ee5d&amp;language=en_US&amp;ref_=as_li_ss_tl" target="_blank" >like this one for $10</a>)</li>
</ul>
<h2 id="install-libraries">Install libraries</h2>
<pre tabindex="0"><code>DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \
  libncursesw5-dev sox git libicu-dev libudev-dev \
  libncurses5-dev libssl-dev  apt-transport-https  dbus \
  apt-utils  ca-certificates  gnupg2  build-essential  bzip2  \
  cmake  curl  gdb  ladspalist  libasound2-dev  \
  libavahi-client-dev  libavahi-compat-libdnssd-dev  \
  libcwiid-dev  libcairo2-dev  libevdev-dev  libfftw3-dev \
  libicu-dev  liblo-dev  liblua5.1-dev  liblua5.3-dev  \
  libreadline6-dev  libxt-dev  luarocks  pkg-config  \
  python-dev  unzip  wget cdbs  libboost-program-options-dev \
  make gcc g++ libmad0-dev  libid3tag0-dev libsndfile1-dev \
  libgd-dev libboost-filesystem-dev libboost-regex-dev \
  python3-pip python3-setuptools python3-wheel vim \
  libboost-dev libsdl2-dev x11vnc xvfb x11-apps imagemagick \
  icecast2 lame espeak ffmpeg vorbis-tools darkice 
</code></pre><h2 id="install-audiowaveform">Install audiowaveform</h2>
<pre tabindex="0"><code>git clone https://github.com/bbc/audiowaveform.git /tmp/audiowaveform &amp;&amp; \
    mkdir -p /tmp/audiowaveform/build &amp;&amp; \
    cd /tmp/audiowaveform/build &amp;&amp; \
    cmake -D ENABLE_TESTS=0 .. &amp;&amp; make &amp;&amp; sudo make install &amp;&amp; \
    audiowaveform --help
</code></pre><h2 id="install-aubio-tools">Install aubio tools</h2>
<pre tabindex="0"><code>git clone https://git.aubio.org/aubio/aubio /tmp/aubio &amp;&amp; cd /tmp/aubio &amp;&amp; \
    make &amp;&amp; cd /tmp/aubio &amp;&amp; sudo ./waf install --destdir=/ &amp;&amp; sudo ldconfig &amp;&amp; \
    cd / &amp;&amp; rm -rf /tmp/aubio &amp;&amp; aubioonset --help
</code></pre><h2 id="install-go">Install Go</h2>
<p>For 64-bit Raspberry Pi:</p>
<pre tabindex="0"><code>wget https://go.dev/dl/go1.19.2.linux-arm64.tar.gz -O /tmp/go.tar.gz &amp;&amp; \
    sudo tar -C /usr/local -xzvf /tmp/go.tar.gz &amp;&amp; \
    rm -r /tmp/go.tar.gz &amp;&amp; \
    /usr/local/go/bin/go version
</code></pre><h2 id="install-ldoc">install ldoc</h2>
<pre tabindex="0"><code>sudo luarocks install ldoc
</code></pre><h2 id="install-jack2">install jack2</h2>
<p>First install the distro-version to get the folders created.</p>
<pre tabindex="0"><code>sudo apt install jack
</code></pre><p>Then install a later version known to work <code>v1.9.19</code>:</p>
<pre tabindex="0"><code>export JACK2_VERSION=1.9.19 &amp;&amp; mkdir -p /tmp/jack2 &amp;&amp; \ 
    wget https://github.com/jackaudio/jack2/archive/v$JACK2_VERSION.tar.gz -O /tmp/jack2/jack2.tar.gz &amp;&amp; \
    cd /tmp/jack2 &amp;&amp; \
    tar xvfz jack2.tar.gz &amp;&amp; \
    cd /tmp/jack2/jack2-$JACK2_VERSION &amp;&amp; \
    ./waf configure --classic --alsa=yes --firewire=no --iio=no --portaudio=no --prefix /usr &amp;&amp; \
    ./waf &amp;&amp; sudo ./waf install &amp;&amp; cd / &amp;&amp; rm -rf /tmp/jack2 &amp;&amp; sudo ldconfig
</code></pre><h2 id="install-supercollider">Install SuperCollider</h2>
<pre tabindex="0"><code>export SUPERCOLLIDER_VERSION=3.12.2 &amp;&amp;  mkdir -p /tmp/supercollider &amp;&amp; cd /tmp/supercollider &amp;&amp; \
    wget https://github.com/supercollider/supercollider/releases/download/Version-$SUPERCOLLIDER_VERSION/SuperCollider-$SUPERCOLLIDER_VERSION-Source.tar.bz2 -O sc.tar.bz2 &amp;&amp; \
    tar xvf sc.tar.bz2 &amp;&amp; cd /tmp/supercollider/SuperCollider-$SUPERCOLLIDER_VERSION-Source &amp;&amp; \
    mkdir -p build &amp;&amp; cd /tmp/supercollider/SuperCollider-$SUPERCOLLIDER_VERSION-Source/build &amp;&amp; \
    cmake -DCMAKE_BUILD_TYPE=&#34;Release&#34; \
          -DCMAKE_INSTALL_PREFIX=/usr/local \
          -DBUILD_TESTING=OFF \
          -DENABLE_TESTSUITE=OFF \
          -DNATIVE=OFF \
          -DINSTALL_HELP=OFF \
          -DSC_IDE=OFF \
          -DSC_QT=OFF \
          -DSC_ED=OFF \
          -DSC_EL=OFF \
          -DSUPERNOVA=OFF \
          -DSC_VIM=OFF \
          .. &amp;&amp; \
    make -j1 &amp;&amp; sudo make install &amp;&amp; cd /
</code></pre><p>While that is installing, you can also install the plugins:</p>
<pre tabindex="0"><code>export SUPERCOLLIDER_VERSION=3.12.2 &amp;&amp;  SUPERCOLLIDER_PLUGINS_VERSION=3.11.1 &amp;&amp; \
    mkdir -p /tmp/sc3-plugins &amp;&amp; cd /tmp/sc3-plugins &amp;&amp; \
    git clone --depth=1 --recursive --branch Version-$SUPERCOLLIDER_PLUGINS_VERSION https://github.com/supercollider/sc3-plugins.git &amp;&amp; \
    cd /tmp/sc3-plugins/sc3-plugins &amp;&amp; mkdir -p build &amp;&amp; \
    cd /tmp/sc3-plugins/sc3-plugins/build &amp;&amp; \
    cmake -DSC_PATH=/tmp/supercollider/SuperCollider-$SUPERCOLLIDER_VERSION-Source \
          -DNATIVE=OFF \
          .. &amp;&amp; \
    cmake --build . --config Release -- -j1 &amp;&amp; \
    sudo cmake --build . --config Release --target install &amp;&amp; \
    cd / &amp;&amp; rm -rf /tmp/sc3-plugins &amp;&amp; sudo ldconfig
</code></pre><h2 id="install-nanomsg">Install nanomsg</h2>
<pre tabindex="0"><code>export NANOMSG_VERSION=1.1.5 &amp;&amp; mkdir -p /tmp/nanomsg &amp;&amp; cd /tmp/nanomsg &amp;&amp; \
    wget https://github.com/nanomsg/nanomsg/archive/$NANOMSG_VERSION.tar.gz -O nanomsg.tar.gz &amp;&amp; \
    tar -xvzf nanomsg.tar.gz &amp;&amp; cd /tmp/nanomsg/nanomsg-$NANOMSG_VERSION &amp;&amp; \
    mkdir -p /tmp/nanomsg/nanomsg-$NANOMSG_VERSION/build &amp;&amp; \
    cd /tmp/nanomsg/nanomsg-$NANOMSG_VERSION/build &amp;&amp; \
    cmake .. &amp;&amp; cmake --build . &amp;&amp; sudo cmake --build . --target install &amp;&amp; \
    cd / &amp;&amp; rm -rf /tmp/nanomsg &amp;&amp; sudo ldconfig
</code></pre><h2 id="install-libmonome">Install libmonome</h2>
<pre tabindex="0"><code>export LIBMONOME_VERSION=1.4.4 &amp;&amp; cd /tmp/ &amp;&amp; wget https://github.com/monome/libmonome/archive/v$LIBMONOME_VERSION.tar.gz -O libmonome.tar.gz &amp;&amp; \
    tar -xvzf libmonome.tar.gz &amp;&amp; cd /tmp/libmonome-$LIBMONOME_VERSION &amp;&amp; \
    ./waf configure --disable-udev --disable-osc &amp;&amp; \
    ./waf &amp;&amp; sudo ./waf install &amp;&amp; \
    cd / &amp;&amp; rm -rf /tmp/libmonome-$LIBMONOME_VERSION &amp;&amp; sudo ldconfig
</code></pre><h2 id="create-credentials">Create credentials</h2>
<p>Edit the <code>/etc/security/limits.conf</code> file and add these two lines:</p>
<pre tabindex="0"><code>@audio   -  rtprio     95
@audio   -  memlock    unlimited
</code></pre><p>Now lets create the <code>we</code> user:</p>
<pre tabindex="0"><code>sudo useradd -g audio we -m -s /bin/bash
sudo usermod -aG video we
sudo adduser we sudo 
sudo mkhomedir_helper we
</code></pre><p>Make sure to add <code>we</code> to the sudoers:</p>
<pre tabindex="0"><code>sudo visudo
# add
we ALL=(ALL) NOPASSWD: ALL
</code></pre><p>Add a password for <code>we</code>:</p>
<pre tabindex="0"><code>sudo passwd we 
</code></pre><p>Very important now to log in as <code>we</code>:</p>
<pre tabindex="0"><code>su we
cd ~
</code></pre><h2 id="install-oh-my-zsh">Install oh-my-zsh</h2>
<p>Now you should always be logged in as <code>we</code>!</p>
<p>This makes things easier.</p>
<pre tabindex="0"><code>ZSH=/home/we/.oh-my-zsh sh -c &#34;$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&#34;
</code></pre><pre tabindex="0"><code>curl https://raw.githubusercontent.com/schollz/dotfiles/master/.zshrc &gt; ~/.zshrc
source ~/.zshrc
</code></pre><h2 id="install-node">Install node</h2>
<p>(You should be logged in as <code>we</code>).</p>
<p>For 64-bit Raspberry pi:</p>
<pre tabindex="0"><code>cd /tmp/ &amp;&amp; wget https://nodejs.org/dist/v16.17.1/node-v16.17.1-linux-arm64.tar.xz -O /tmp/node.tar.xz &amp;&amp; \
  mkdir -p /home/we/node &amp;&amp; tar -xJf /tmp/node.tar.xz -C /home/we/node &amp;&amp; \
  mv /home/we/node/node-*/* /home/we/node/ &amp;&amp; \
  mkdir -p /home/we/node/node_modules &amp;&amp; \
  npm config set prefix &#34;/home/we/node/node_modules&#34; &amp;&amp; \
  npm install -g npm yarn &amp;&amp; npm -v &amp;&amp; node -v
</code></pre><h2 id="install-maiden">Install maiden</h2>
<p>(You should be logged in as <code>we</code>).</p>
<p>This step uses a specially patched version of <code>maiden</code> that doesn&rsquo;t use <code>dbus</code>.</p>
<pre tabindex="0"><code>export MAIDEN_TAG=ce4471e25a45c87040817c0619f3596fa43060aa &amp;&amp;
    export MAIDEN_REPO=https://github.com/schollz/maiden.git &amp;&amp; git clone $MAIDEN_REPO maiden_src &amp;&amp; \
     cd maiden_src &amp;&amp; \
     git checkout $MAIDEN_TAG &amp;&amp; \
     make release-local &amp;&amp; \
     tar -xvf dist/maiden.tgz -C /home/we &amp;&amp; \
     /home/we/maiden/project-setup.sh
</code></pre><h2 id="install-matron">Install matron</h2>
<p>(You should be logged in as <code>we</code>).</p>
<p>At this step you might need a little more than 1G of ram to compile. Create a small swpa in case you haven&rsquo;t to make sure it compiles fine:</p>
<pre tabindex="0"><code>sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
</code></pre><p>Now you can build norns:</p>
<pre tabindex="0"><code>export NORNS_TAG=2faa96bd763b69e4b840adc4e2ee8e58e00522f0 &amp;&amp; \
  export NORNS_REPO=https://github.com/monome/norns.git &amp;&amp; \
  cd /home/we/ &amp;&amp; git clone $NORNS_REPO &amp;&amp;  cd /home/we/norns &amp;&amp; \
  git checkout $NORNS_TAG &amp;&amp;  git submodule update --init --recursive &amp;&amp; \
  ./waf configure --desktop &amp;&amp; ./waf build --desktop
</code></pre><p>Once installed, the norns source needs to be patched:</p>
<pre tabindex="0"><code>sed -i &#39;s/norns.disk/100000/g&#39; /home/we/norns/lua/core/menu/tape.lua
sed -i &#39;s/screensaver.time = 900/screensaver.time = 90000000/g&#39; /home/we/norns/lua/core/screen.lua
</code></pre><p>Also you need to a copy a file to the SuperCollider extensions:</p>
<pre tabindex="0"><code>mkdir -p ~/.local/share/SuperCollider/Extensions
cp /home/we/norns/sc/norns-config.sc ~/.local/share/SuperCollider/Extensions/
</code></pre><h2 id="install-startup-files">Install startup files</h2>
<p>(You should be logged in as <code>we</code>).</p>
<pre tabindex="0"><code>cd /home/we &amp;&amp; git clone https://github.com/schollz/norns-desktop &amp;&amp; \
    cd /home/we/norns-desktop &amp;&amp; chmod +x *sh &amp;&amp; \
    cp restart_* /home/we/norns/ &amp;&amp; \
    cp /home/we/norns-desktop/matronrc.lua /home/we/norns/ &amp;&amp; \
    go build -v -x
</code></pre><p>Setup the main directories and add some scripts:</p>
<pre tabindex="0"><code>mkdir -p ~/dust/data &amp;&amp; mkdir -p ~/dust/audio &amp;&amp; \
    mkdir -p ~/dust/code &amp;&amp; cd /home/we/dust/code &amp;&amp; \
    git clone https://github.com/tehn/awake &amp;&amp; \
    git clone https://github.com/northern-information/dronecaster &amp;&amp; \
    git clone https://github.com/schollz/l_ll__l_
</code></pre><h2 id="run-norns">Run norns!</h2>
<p>(You should be logged in as <code>we</code>).</p>
<p>You can run norns in the full-mode (with playback and recording) <em>only</em> if you have a 2x2 USB audio interface. Otherwise you need to run in playback mode only.</p>
<p>By default the script is for playback mode, only:</p>
<pre tabindex="0"><code>/home/we/norns-desktop/start_norns_pi.sh
</code></pre><p>If you want to use a 2x2 USB audio interface, you need to change <code>start_norns_pi.sh</code> and uncomment the line that says <code>2x2 USB interface</code> and comment the line that says <code>playback only</code>.</p>
<p>Open <code>&lt;your pi IP address&gt;:8889</code> to see your norns and interact with it. Open <code>&lt;your pi IP address&gt;:5000</code> to use maiden to install scripts and edit things and restart the system.</p>
<h3 id="troubleshooting">Troubleshooting</h3>
]]></description>
    </item>
    
    <item>
	    <title>Paracosms</title>
	    <image>https://schollz.com/img/dali.png</image>
      <link>/music/paracosms/</link>
      <pubDate>Sat, 02 Jul 2022 05:00:46 -0700</pubDate>
      
      <guid>/music/paracosms/</guid>
      <description><![CDATA[<p>For the past three months I&rsquo;ve been trying to make entire songs using <em>only</em> pre-recorded samples which has resulted in a new album <a href="https://infinitedigits.bandcamp.com/album/paracosms" target="_blank" >available now on Bandcamp</a>. There are a hundred songs on this album, all the result of combining samples using some software that automatically generates songs from sample pools.</p>
<p>Each song combines over a dozen samples, each from a different artist. <strong>Each song is a <em>paracosm</em>: a random imagined world emerging from a combination of creators</strong>.</p>
<div style="background:#ffe; padding:1.5em; border-radius:1em; border: 0.1em solid #000; ">
The last song I made, and the last song on the album.
<br>
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1729070624/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/track=2076225503/transparent=true/" seamless><a href="https://infinitedigits.bandcamp.com/album/flipped-samples">flipped samples by infinite digits</a></iframe>
</div>
<p>Though there are many songs, they all follow a similar theme which I would vaguely describe as glitched ambient jungle usually with instrumentation from synths, strings, pianos and saxophones. The album can be listened in order from track 1 to track 100 - each song changes key from one to next according to <a href="https://en.wikipedia.org/wiki/Circle_of_fifths" target="_blank" >the circle of fifths</a> to promote a sense of progression. Or listen in any order you want.</p>
<h2 id="the-process">the process</h2>
<p>My goal for this album was to utilize a process that used <em>only</em> pre-recorded samples.  I usually avoid this type of process, for various reasons. (Mostly I like performance-based music akin to my musical-origins of playing piano). But my motivation for this project was to immerse myself in my own personal reluctance.</p>
<p>To start, I needed samples. I curated a catalog of pre-recorded samples by randomly selecting things I liked from <a href="https://splice.com" target="_blank" >splice.com</a>. I spent $110 to collect all the samples I needed to make the entire album.</p>
<p>The process of manipulating samples into a song can be laborious - it requires taking a sample in a different key or tempo and stretching it to the target tempo, re-pitching it, trimming it to the right length, adding the right effects, splicing it in with crossfades in the right way, etc. For a hundred song album, this would mean doing these microedits <em>thousands</em> of times.</p>
<p>To avoid the tedium in using samples, I wrote a program I call <code>raw</code> (<a href="https://github.com/schollz/raw" target="_blank" >github.com/schollz/raw</a>). The &ldquo;daw&rdquo; is familiar as the &ldquo;digital audio workstation&rdquo; but this audio workstation made almost all decisions stochastically so I call it the <em>random</em> audio workstation&quot; (e.g. <code>raw</code>). <code>raw</code> randomly chooses which samples to use, how to layer the samples, how to do transitions, and which effects to add. The decisions are all based around probabilities so the same set of samples might create an entirely different song. I have control over the probabilities but not much else.</p>
<p>I didn&rsquo;t write the entire program from scratch - <code>raw</code> was built upon freely available open-source music tools - <a href="http://sox.sourceforge.net/" target="_blank" >sox</a> (the music swiss army knife) and <a href="https://github.com/supercollider/supercollider/" target="_blank" >SuperCollider</a> (a powerful music coding language). I used <code>sox</code> to perform all the <a href="https://github.com/schollz/raw/blob/main/src/sox/sox.go" target="_blank" >splicing / stretching operations / effects</a>, and I used SuperCollider for additional special effects like <a href="https://github.com/schollz/raw/blob/main/src/supercollider/raw.sc" target="_blank" >special gated delays and filter ramps</a>.</p>
<p>(<code>raw</code> is a more sophisticated version of a norns script I wrote called <a href="https://llllllll.co/t/sampswap" target="_blank" >sampswap</a> which itself is a more sophisticated version of a norns script I wrote called <a href="https://llllllll.co/t/makebreakbeat/" target="_blank" >makebreakbeat</a>).</p>
<h2 id="more-process">more process</h2>
<p>I realize that my process of pooling random samples and juxtaposing them
randomly is a bit alternative so I thought I&rsquo;d describe in detail pieces of it.</p>
<div style="background:#ffe; padding:1.5em; border-radius:1em; border: 0.1em solid #000; ">
Here is the first song I generated using this weird auto-generated sample process:
<br>
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1729070624/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/track=2285865193/transparent=true/" seamless><a href="https://infinitedigits.bandcamp.com/album/paracosms">paracosms by infinite digits</a></iframe>
</div>
<p>I have all of my downloaded samples in a single directory. To make matters easier I renamed each file to include its tempo. I auditioned songs by listening to them back in a web browser that pointed to the directory of samples. This little website I made that has code that is so poorly written I don&rsquo;t bother to share it.</p>
<p><img src="/img/sampledb.PNG" alt="sampledb"></p>
<p>The screenshot of the website for sample auditioning is above. Its a very basic UI that I can scroll through my samples in parallel and play them back to see which pool of samples might work well together. Its really dumb simple - written in Vue and it generates a bash script that will copy the necessary files into a new directory so that they can be utilized by <code>raw</code>.</p>
<p>After that I simply run <code>raw</code> which generates 4-5 stems: one for drums, bass, chords, melody and sometimes vocals. This process takes 1-2 minutes to generate everything. The end result are 4-5 music files, each is a single stem for each part (drums, bass, chords, etc.), and each is the length fo the final song.</p>
<p><img src="/img/raw.png" alt="raw"></p>
<p>The last thing to do is to take these stems and mix them together in a way that makes sense. I usually prefer to make the drum sounds louder and vocals louder in the mix. To do this I just open them in Ableton and edit the mixing levels.</p>
<p><img src="/img/ableton.PNG" alt="ableton"></p>
<p>In Ableton I don&rsquo;t do much - the stems each contain the entirety of one instrument of the song. After that I listen through, fix any pops that occurred from the splicing program, and render it!</p>
<p>Each song took between 15-30 minutes to go through the entire process. I probably generated around 300 songs for this album (rejecting &gt;60% of them) so it was easily about 150 hours of work. If I wasn&rsquo;t automating most of the steps I imagine it would&rsquo;ve taken much much longer.</p>
<h2 id="the-art-in-the-art">the art in the art</h2>
<p>during this process I was thinking - what is the <em>art</em> in
this process? I had no part in creating the samples - they are all pre-recorded from other musicians who sent them to splice.com. and though I wrote software to make the songs, the software itself works through randomness and makes the main choices (which samples to layer, which effects, when) without my input. So where&rsquo;s the artist input?</p>
<p>late into the process I found that I could maintain a sense of &ldquo;artistic
direction&rdquo; by harnessing three things within my  control:</p>
<ul>
<li>curating of the pool of samples.</li>
<li>choosing of the tempo.</li>
<li>mixing the final five stems.</li>
</ul>
<p>They seem fairly subtle, but actually in my process they had a big impact.</p>
<p>The software I wrote will layer samples from the pool randomly, but the pool of
samples is created by me. it took a few dozen songs to get the hang of this,
but I found I would need to select a pool of samples that can intermingle in a
variety of ways, otherwise it can end up sounding like multiple radio stations playing simultaneously.</p>
<p>Also I found that my chosen tempo made a big difference - sometimes tempos were fast and sounded like a mishmash of radio stations and sometimes they were so slow they just plodded. I spent a lot of time just trying to figure out the right tempo for each track. Usually I generated a song two or three times to get one that I liked.</p>
<p>Another realization I had was that this whole process is basically a lesson in mixing. my software didn&rsquo;t mix them - it just outputs things at the levels they were prerecorded. I could get better results by working to mix them (really just manipulating five numbers, but boy those numbers make a big difference).</p>
<h2 id="enjoy">enjoy</h2>
<p>In the end I enjoyed the process (though only because it was automated) and I am enjoying the resulting album. This album has been my go-to listening for months and I&rsquo;ve listened all the way through almost a dozen times. maybe you will enjoy it too. feel free to pwyw or just download it with a code:</p>
<details><summary>bandcamp codes</summary>
<p>
wcse-cqst
pj4b-w8cb
8fsl-c24w
2d44-xdvs
h6su-bcdl
h34x-xhwe
hdes-j5d3
q46c-grss
fc3c-34hd
whxq-cmpc
cqc9-wtbw
crhu-cbl4
sxcx-xcva
s4ps-jwtb
psfs-yw3x
hxpe-wpc8
jc9f-c2s2
6s2l-wdhc
n4lj-hcpw
les6-5c3s
c6h2-bwdl
cncd-xxwe
cdjc-vpd3
phg4-g2ss
rcu4-3lhd
dswj-cjpc
6fhx-wbhw
uw4s-hwms
xpps-5wyd
49fc-3s2h
43d4-c4wv
cxqq-wac8
e487-cbs2
gsnx-wchc
2hwp-hwpw
deh8-5w3s
s8c3-3sdl
h4j4-jsyb
jcb4-yd2x
fhxh-bhsq
l4cj-wjct
qesr-cysb
g74d-w3hw
3lsc-xsms
wpq4-esyd
s6r4-3d2h
s3lh-clwv
sdpj-wec8
74us-bx4d
xcwp-wvec
4jhg-w8rw
4fcn-crx4
4dhl-xdma
cheh-jdyb
ec7h-yc2x
bsls-bxsq
dhsp-wvct
pe4b-w8sb
8rsl-crhw
xq49-5d2s
hgsu-bdwl
4u4x-xhde
4les-jew3
qs6s-y3cs
bh3c-344d
wcxq-cqec
cpc9-wtrw
sfhu-c2x4
swcx-xcma
ssps-jcyb
hnls-wxdv
hwpe-wps8
qh9f-crc2
942l-wd4c
nslj-hdew
wjs6-5c2s
cgh2-bcwl
c2cd-xxde
sljc-j5w3
pcg4-grcs
xhhq-wmst
jqc7-ctcb
6rhx-wb4w
nx4s-hcvs
lqps-5wtd
48fc-bw3h
cud4-c4dv
cwqq-was8
es87-c2c2
</p>
</details>
<p>The codes can be redeemed at <a href="https://infinitedigits.bandcamp.com/yum" target="_blank" >https://infinitedigits.bandcamp.com/yum</a> .</p>
<h2 id="btw-the-cover-art">btw, the cover art</h2>
<p>The artwork for the album utilizes <a href="https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb" target="_blank" >Disco Diffusion</a>, an AI that can create artworks from text. The text I chose is artwork of Savlador&rsquo;s Dali&rsquo;s terrariums. Dali wrote extensively about the importance of keeping spider terrariums which were like little paracosms meant to deliver inspiration.</p>
]]></description>
    </item>
    
    <item>
	    <title>One-line drawings</title>
	    <image>https://schollz.com/img/oneline/people.png</image>
      <link>/tinker/one-line/</link>
      <pubDate>Sat, 09 Apr 2022 08:00:46 -0700</pubDate>
      
      <guid>/tinker/one-line/</guid>
      <description><![CDATA[<p>There was a well-known painter in 1878 named <a href="https://babel.hathitrust.org/cgi/pt?id=hvd.fl4mls&amp;view=1up&amp;seq=27" target="_blank" >James McNeill Whistler who was sued</a> for the prices of a painting because it had been created in only <em>two days</em>. He was ordered in court to explain the cost:</p>
<blockquote>
<p>“Oh, two days! The labour of two days, then, is that for which you ask two hundred guineas!”</p>
<p>“No;—I ask it for the knowledge of a lifetime.”</p>
</blockquote>
<p>This quote about charging high prices for small artworks eventually got <a href="https://quoteinvestigator.com/2018/01/14/time-art/" target="_blank" >attributed to Picasso apocryphally</a>. Picasso&rsquo;s paintings and drawings are among the most valuable, though he has a great many drawings that are exceedingly simplistic. In fact, Picasso create a whole <a href="https://www.pablopicasso.net/drawings/" target="_blank" >series of drawings using sometimes just a single-line</a>.</p>
<p><img src="https://ae01.alicdn.com/kf/S01c634a3fe0940e9b101cf52f16162cfI/Pablo-Picasso-Print-Animals-Abstract-Art-One-Line-Drawings-Painting-Pictures-Wall-Art-Canvas-Prints-Sketches.jpg" alt="Picasso&rsquo;s one line drawings"></p>
<p>These drawings are simple and beautiful. I got to thinking about reproducing them. But instead of printing them with an inkjet printer, I was interested in drawing them in the style they were meant to be created - using <em>one line</em>.</p>
<h2 id="converting-a-drawing-to-a-one-line-drawing">Converting a drawing to a one-line drawing</h2>
<p>The drawings I am interested in reconstructing are done in one (but sometimes two or three) lines. To figure out the line, I would need to do several steps. I will be starting from a plain image - like a <code>.png</code> or <code>.jpg</code> file. The <code>.png</code>/<code>.jpg</code> is called a raster image. That is - it has information about pixels and intensities but no information about <em>lines</em>.</p>
<p>I would need a program to trace out the lines in the image and convert it to a coordinate system describing lines. This is what vector graphics are for - and there is a image format already encompassing it called <a href="https://developer.mozilla.org/en-US/docs/Web/SVG" target="_blank" >scalable vector graphics (SVG)</a>.</p>
<p>Once I have the SVG file with the lines, I would need to gather up all the coordinates and the lines and consolidate the lines so that it creates a single seamless line.</p>
<h2 id="converting-a-raster-image-to-vector-graphics">Converting a raster image to vector graphics</h2>
<p>I am going to start with a simple line drawing - this one-line drawing of a person.</p>
<center>
<img src="/img/oneline/person.png" style="max-width:200px">
</center>
<p>Its pretty clear to see the one line with our human eyes, the challenge here will be to get a computer to also recognize it. The first thing to do is to convert it to a SVG, and we can use two programs: <a href="https://imagemagick.org/script/download.php" target="_blank" ><code>imagemagick</code></a> and <a href="https://github.com/autotrace/autotrace" target="_blank" ><code>autotrace</code></a>.</p>
<p>First, using <code>imagemagick</code>&rsquo;s <code>convert</code> command, the image is thresholded and output into a format nessecary for the next tool.</p>
<pre tabindex="0"><code>&gt; convert person.jpg -threshold 60% person.tga
</code></pre><p>Next the resulting image <code>person.tga</code> can be converted using <code>autotrace</code> into a vector graphic file using the &ldquo;centerline&rdquo; algorithm that finds the skeleton center line.</p>
<pre tabindex="0"><code>&gt; autotrace -output-file person.svg --output-format svg --centerline person.tga
</code></pre><p>Great, now we have an SVG file (<code>person.svg</code>) to work with for the next step.</p>
<h2 id="parsing-svg-and-consolidating-lines">Parsing SVG and consolidating lines</h2>
<p>Parsing a SVG is standard thing to do and easy to do with lots of tools. The actual SVG content contains a list of coordinates in &ldquo;paths&rdquo; which are denoted by <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths" target="_blank" >&ldquo;line commands&rdquo;</a>. Some of the coordinates are Bezier curves definitions which themselves need to be converted to absolute coordinates.</p>
<p>Also an SVG that looks like it contains only one line may not in fact contain one line. For example, part of the <code>person.svg</code> path looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-svg" data-lang="svg"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl">...d=&#34;M47 31L45 33L47 31M127 83L139.821...
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a></span><span class="cl">      ^                 ^                  
</span></span></code></pre></div><p>There are special letters (like &ldquo;<code>M</code>&rdquo;) denoted where the path should move before starting the next stroke. The presence of these moves means that the SVG I have is actually multiple lines and not a single line.</p>
<p>I need to join these lines together, but first I need to re-order them in the correct order. To re-order the lines I wrote a simple algorithm that uses gradient descent to minimize the distances between subsequent end and start points of each line. This randomly looks through all possible combinations of lines until it finds the best one (though its not guaranteed to be the best).</p>
<p>Once I have that I can output a little animation showing the way the line moves by following the coordinates of my freshly ordered lines:</p>
<center>
<video src="/img/oneline/person.webm" autoplay loop style="max-width:200px">
</center>
<p>Now that I have coordinates I can get to the drawing.</p>
<h2 id="now-for-the-actual-drawing">Now for the actual drawing</h2>
<p>This year my goal is to learn CAD design and to facilitate that I invested in a 3D printer. Of course 3D printers use plastic to make 3D things, but ironically one of my first 3D prints was to make a pen holder so I could make 2D things - I printed out a compatible pen holder (<a href="https://www.thingiverse.com/thing:4764350" target="_blank" >Thingiverse thing:4764350</a> designed by <a href="https://www.thingiverse.com/366pete/designs" target="_blank" >Pete Manville</a>).</p>
<p>Now to convert the SVG to Gcode. Gcode essentially produces an instruction to move in the X/Y/Z axis. Using special instructions (&quot;<code>G90</code>&quot; and &ldquo;<code>G91</code>&rdquo;) you can also switch between absolute/relative coordinate systems. can communicate in absolute and relative coordinates. I can just parse my SVG coordinates, and for each new line it will switch to relative coordinates, move the pen up (in the Z-axis), switch to absolute coordinates, move to the new line start, switch to relative coordinates, then move back down, and finally switch to absolute coordinates and go ahead and draw. It looks something like this:</p>
<pre tabindex="0"><code class="language-gcode" data-lang="gcode">G91 ; Set coordinates to relative
G1 Z10 F1000 ; raise pen
G90 ; Set coordinates to absolute
G1 X42.712 Y51.772 F1000
G91 ; Set coordinates to relative
G1 Z0 F1000 ; lower pen
G90 ; Set coordinates to absolute
G1 X41.137 Y52.487 F1000
</code></pre><p>Since 3D/CNC machines talk to computers over Serial, its simple enough for my program to simply upload the Gcode after converting the file so that it can draw.</p>
<center>
<video src="/img/oneline/personprint.mp4" autoplay loop style="max-width:100%">
</center>
<p>And that&rsquo;s it!</p>
<p>As a bonus I can use this same code to draw my own SVG&rsquo;s and upload them to print really easily with this workflow.</p>
<h2 id="source-code">Source code</h2>
<p>The source code for everything in this post can be found on my Github: <a href="https://github.com/schollz/svg2gcode" target="_blank" >schollz/svg2gcode</a>. The basic workflow is as follows:</p>
<pre tabindex="0"><code>&gt; # imagemagick to threshold image
&gt; convert IMAGE -resize 300x -background White -gravity center -threshold 60% 1.tga
&gt; # autotrace to get center line 
&gt; autotrace -output-file potrace.svg --output-format svg --centerline 1.tga
&gt; # convert to Gcode
&gt; svg2gcode convert --debug --png --in potrace.svg --out IMAGE -x 0 -y 0 --width 90 --height 90 --simplify 0.005 --consolidate 0.07 --min-length 0.03 --animate
&gt; # upload to printer
&gt; svg2gcode upload IMAGE.gcode
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Random music videos</title>
	    <image>https://schollz.com/img/deepai.jpg</image>
      <link>/tinker/rand/</link>
      <pubDate>Sat, 26 Mar 2022 08:00:46 -0700</pubDate>
      
      <guid>/tinker/rand/</guid>
      <description><![CDATA[<p>Lately I&rsquo;ve been generating &ldquo;ambient jungle&rdquo; using a script I wrote (<a href="/blog/sampswap" >sampswap</a>) to probabilistically add effects to audio. The generated music pieces are all composed of random samples with random effects so I thought it&rsquo;d be relevant to make random music videos.</p>
<p>I found <a href="https://github.com/msieg/deep-music-visualizer" target="_blank" >Deep Music Visualizer</a>, that uses <a href="https://arxiv.org/abs/1809.11096" target="_blank" >the BigGAN neural network</a>, which is great for generating tempo and pitch synced music videos.</p>
<p>Here&rsquo;s some example videos with some short snippets of music I generated:</p>
<p align="center">
<center>
<blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/tv/CbXhpCoFlRC/?utm_source=ig_embed&amp;utm_campaign=loading" data-instgrm-version="14" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"> <a href="https://www.instagram.com/tv/CbXhpCoFlRC/?utm_source=ig_embed&amp;utm_campaign=loading" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"> <div style=" display: flex; flex-direction: row; align-items: center;"> <div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div> <div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g><path d="M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631"></path></g></g></g></svg></div><div style="padding-top: 8px;"> <div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;">View this post on Instagram</div></div><div style="padding: 12.5% 0;"></div> <div style="display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;"><div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);"></div> <div style="background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;"></div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);"></div></div><div style="margin-left: 8px;"> <div style=" background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;"></div> <div style=" width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)"></div></div><div style="margin-left: auto;"> <div style=" width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);"></div> <div style=" background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);"></div> <div style=" width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);"></div></div></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;"></div></div></a><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/tv/CbXhpCoFlRC/?utm_source=ig_embed&amp;utm_campaign=loading" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by infinitedigits (@infinitedigits)</a></p></div></blockquote></center> <script async src="//www.instagram.com/embed.js"></script>
</p>
<p>Its actually easy to make this style of video and it can be done for free! You also don&rsquo;t need to download any software to do it - it can all be done in the browser. Here&rsquo;s the tutorial on how to make your own deep AI music videos with your own music.</p>
<h1 id="7-steps-to-make-deep-dream-music-videos">7 steps to make deep dream music videos</h1>
<h2 id="1-get-a-audio-file">1. Get a audio file</h2>
<p>This will probably work best with music, but any audio file will do. Make sure it is a mp3 though, and <strong>rename the audio to &ldquo;1.mp3&rdquo;</strong>. This is important.</p>
<p>(Its possible to use files with different names but if you have a different name you have to change some things further down but its more complicated).</p>
<h2 id="2-download-a-colab-notebook">2. Download a colab notebook</h2>
<p>Now use this link to download the &ldquo;notebook&rdquo;: <a href="https://gist.github.com/schollz/1741ac03a19750fadef7e5cd0add0f29" target="_blank" ><code>deep_dreams_music_video.ipynb</code></a>. Click the &ldquo;Download ZIP&rdquo; button on that page to download it. Unzip the archive and note where it is on the computer. We will use the file in the next step.</p>
<h2 id="3-setup-google-colab">3. Setup Google Colab</h2>
<p>We are going to use Google&rsquo;s &ldquo;Colab notebooks&rdquo; to generate the videos. This will allow you to generate the videos without having to install anything (and the video generation is fast because you can use Google&rsquo;s GPU). And its free.</p>
<p>You have to have a Google account for this. Once you do, goto <a href="https://colab.research.google.com" target="_blank" >colab.research.google.com</a>. Once you are on that page you might be greeted by a box such as below - if not in the upper left-hand corner you can click &ldquo;File&rdquo; and then &ldquo;Open&rdquo;. Then you should see this box:</p>
<p><img src="/img/colab.png" alt="colab"></p>
<p>You&rsquo;ll need to click the tab that says &ldquo;Upload&rdquo;. Now you go ahead and hit &ldquo;Browse&hellip;&rdquo; and select the file you downloaded and unzipped in the last step. Now you should have it loaded in your window!</p>
<h2 id="4-change-runtime">4. Change runtime</h2>
<p>The default runtime processor is CPU, we need to use GPUs. Click &ldquo;Runtime&rdquo; and click &ldquo;Runtime Type&rdquo; and then select &ldquo;GPU&rdquo; where it says &ldquo;Hardware accelerator&rdquo;. Then hit &ldquo;Save&rdquo;.</p>
<p><img src="/img/colab2.png" alt="colab"></p>
<h2 id="5-load-the-notebook">5. Load the notebook</h2>
<p>Now we can load the notebook. You can run each section of the notebook, one at a time, in sequence. Start with the first two sections.</p>
<p><img src="/img/colab3.png" alt="colab"></p>
<p><img src="/img/colab4.png" alt="colab"></p>
<h2 id="6-upload-your-music">6. Upload your music</h2>
<p>Now we should be at the third &ldquo;cell&rdquo; of the notebook. Run it like you did in the last two steps. After you run it, a little button will appear that you can use to upload your music.</p>
<p><img src="/img/colab5.png" alt="colab"></p>
<p>This button says &ldquo;Browse&rdquo; and you can click on it and upload your music file &ldquo;1.mp3&rdquo;. Wait a few seconds for it to upload.</p>
<h2 id="7-generate-a-music-video">7. Generate a music video!</h2>
<p>Now the final part is to run the last cell of the notebook by clicking play. It will use the music file you uploaded to generate a random music video. Once its down it will be downloaded to your computer. This might take a few minutes, depending on the size of the file.</p>
<p><img src="/img/colab6.png" alt="colab"></p>
<p>You can do this step as many times as you want - it will generate a new random video each time. You don&rsquo;t need to run all the other cells if you want to make a new one either! Just run this last cell and wait a few minutes for the new video to download to your computer.</p>
<h2 id="8-optional-modify-the-kinds-of-images">8. (optional) Modify the kinds of images</h2>
<p>You <em>can</em> change the types of images shown, to an extent. This is totally optional - by default the classes of images will be random. But you can edit one line of code to try to steer the randomness by allowing only certain classes of images.</p>
<p>First, <a href="https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a" target="_blank" >check out this list</a> of available image classes. Each one has a number, and you need to write down the numbers of the classes you want. For example, if I wanted &ldquo;hen&rdquo; and &ldquo;vulture&rdquo; type images I would write down &ldquo;8&rdquo; and &ldquo;23&rdquo;.</p>
<p>Then change one line of code in the last cell. Instead of</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="n">cls1000</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">))</span>
</span></span></code></pre></div><p>change it to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="n">cls1000</span> <span class="o">=</span> <span class="p">[</span><span class="mi">8</span><span class="p">,</span><span class="mi">23</span><span class="p">]</span><span class="o">*</span><span class="mi">12</span>
</span></span></code></pre></div><p>I used &ldquo;8&rdquo; and &ldquo;23&rdquo; because I wanted hens and vulture type classes. But you could do any numbers you want, and choose between 2 and 12 numbers. Keep the <code>*12</code> there otherwise it won&rsquo;t have enough classes to compute. Try generating with that and you should get images that revolve mostly around those selected classes.</p>
<h2 id="enjoy">Enjoy</h2>
<p>If you have any questions, feel free to reach out to me through Instagram or <a href="/" >through my website</a>. If you enjoy my music, check out <a href="https://infinitedigits.bandcamp.com" target="_blank" >the rest of my music on Bandcamp</a>. If you enjoy my coding projects, consider <a href="https://github.com/sponsors/schollz" target="_blank" >becoming a sponsor</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Stream your audio</title>
	    <image>https://schollz.com/img/stream.png</image>
      <link>/tinker/stream/</link>
      <pubDate>Thu, 17 Mar 2022 08:00:46 -0700</pubDate>
      
      <guid>/tinker/stream/</guid>
      <description><![CDATA[<p>What if it was really easy to stream audio from your computer? Like, so easy that you just double click a program and you are streaming? That&rsquo;s what I wanted, so I built it.</p>
<p><img src="/img/streamshell.PNG" alt="streamshell"></p>
<p>This program runs on Windows, Mac OS, and Linux and will directly stream from any audio interface on your computer to a website I setup called <a href="https://streammyaudio.com" target="_blank" >streammyaudio.com</a>.</p>
<p>You can get started by <a href="https://github.com/schollz/streamyouraudio/releases/latest" target="_blank" >downloading a release</a> for your computer and double-clicking it. The software automatically detects your audio devices and creates high quality mp3 streams (up to 260 kpbs) and forwards them the streaming server.</p>
<h2 id="anti-social-media">Anti-social media</h2>
<p>Streaming high-quality audio from your computer to the internet shouldn&rsquo;t be hard but it doesn&rsquo;t seem to be easy.</p>
<p>Pretty much every social media site lets you share audio and video streams (e.g. Twitch, Clubhouse, YouTube, Facebook, Instagram, Discord). But each of these social media sites are now <a href="https://en.wikipedia.org/wiki/Closed_platform" target="_blank" >walled gardens</a> that prevent non-subscribers from accessing content and to force people to buy into their platform (sometimes literally). What if I just want to share something without making an account?</p>
<p>Also the social media audio streaming is often poor quality. These social media sites sacrifice the audio quality to improve the video quality and lack the ability to modify the settings. What if I want to forego video and just have really high quality audio?</p>
<p>I&rsquo;m not aware of a simple audio streaming solution that just works, and doesn&rsquo;t require making accounts and can deliver good audio quality to anyone with a link on the internet.</p>
<h2 id="open-source-audio-streaming-is-here">Open-source audio streaming is here</h2>
<p>It turns out we have high-quality free technology at our fingertips: <a href="https://ffmpeg.org/" target="_blank" >ffmpeg</a>. Normally <code>ffmpeg</code> is used for videos, but is great for converting between audio codecs and is also a great cross-platform way of capturing audio from any audio device. The only other thing you need then is a way to send the mp3 data to a server. And we can use another ubiquitous tool for that: <a href="https://curl.se/" target="_blank" >curl</a>. Although, in the final version of the streaming software I just rewrote the needed part of <code>curl</code>.</p>
<p>As I mentioned before in <a href="/blog/radio" >my blog</a>, then all you need to send out mp3 data from your microphone is a simple line of code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl">&gt;ffmpeg -f alsa -i hw:0 -f mp3 - <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl"><span class="se"></span>    curl -s -k -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T - <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a></span><span class="cl"><span class="se"></span>    <span class="s2">&#34;https://streamyouraudio.com/YOURSTATIONNAME.mp3?stream=true&amp;advertise=true&#34;</span>
</span></span></code></pre></div><p>But not everyone has a linux computer nor wants to know about lines of code they have to type. A better solution would be one that you just click and run, and that&rsquo;s what <a href="https://streammyaudio.com/" target="_blank" >I&rsquo;ve released</a>.</p>
<p>I use it to share streams of my piano performances, or sometimes just stream some of my music so I can listen to it elsewhere. Whatever the case it works great, is easy, and cheap. The server running all the code costs only a few dollars every year (if you&rsquo;d like to support the cost, <a href="https://github.com/sponsors/schollz" target="_blank" >consider sponsoring me</a>).</p>
<p>In any case, I never have to Google &ldquo;how do I stream my audio&rdquo; again.</p>
]]></description>
    </item>
    
    <item>
	    <title>sampswap</title>
	    <image>https://schollz.com/img/sampswapins.jpg</image>
      <link>/tinker/sampswap/</link>
      <pubDate>Tue, 15 Mar 2022 08:00:46 -0700</pubDate>
      
      <guid>/tinker/sampswap/</guid>
      <description><![CDATA[<p><a href="https://github.com/schollz/sampswap" target="_blank" ><em>sampswap</em></a> is a Lua script utility/wrapper around <a href="http://sox.sourceforge.net/sox.html" target="_blank" >sox</a> that makes it easy to swap pieces within a sample while adding effect to them. sox is an incredible tool that can be employed to perform a lot of audio feats, including splicing, trimming, merging, adding effects, detecting silence, equalizing, amplifying, etc. After playing around with it for awhile I realized I could mix and match these operations randomly on audio loops to produce new audio loops, akin to <a href="https://en.wikipedia.org/wiki/Breakbeat" target="_blank" >breakbeat music</a>.</p>
<p>The heavy-lifting is done by <a href="http://sox.sourceforge.net/sox.html" target="_blank" >sox</a> but I employed <a href="https://www.lua.org/" target="_blank" >Lua</a> to create pure functions that can perform the sox commands on audio files. For example, a function to trim the silence from both ends of an audio file is declared as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="kr">function</span> <span class="nc">audio</span><span class="p">.</span><span class="nf">silence_trim</span><span class="p">(</span><span class="n">fname</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl">  <span class="kd">local</span> <span class="n">fname2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="s2">&#34;sox &#34;</span><span class="o">..</span><span class="n">fname</span><span class="o">..</span><span class="s2">&#34; &#34;</span><span class="o">..</span><span class="n">fname2</span><span class="o">..</span><span class="s2">&#34; silence 1 0.1 0.025% reverse silence 1 0.1 0.025% reverse&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4">4</a></span><span class="cl">  <span class="kr">return</span> <span class="n">fname2</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5">5</a></span><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>My idea to produce &ldquo;generative&rdquo; breakbeat music was to take a loop and perform operations randomly, in sequence, so that the loop itself has memory of each operation and thus the result can be more complex than utilizing the original file for each.</p>
<p>When copying and pasting audio its important to be aware of the boundaries. Often the boundaries are &ldquo;merged&rdquo; by crossfading some &ldquo;excess&rdquo; region on the outside of the regions you are pasting. For example, from the sox docs:</p>
<pre tabindex="0"><code>      length1   excess
    -----------&gt;&lt;---&gt;
    _________   :   :  _________________
             \  :   : :\     
              \ :   : : \     
               \:   : :  \     
                *   : :   *  
                 \  : :   :\    
                  \ : :   : \     
    _______________\: :   :  \_________
                      :   :   
                      &lt;---&gt;   
                      excess  
</code></pre><p>Its a bit like cutting and pasting two tape loops together. Practically speaking, this means that when copying a loop you actually need to copy a little bit extra for both sides and paste it at a position slightly before, so that it gets into the correct location and correctly crossfades. This bit is probably the most complicated operation as it requires multiple sox commands.</p>
<details><summary><code>audio.copy_and_paste(...)</code></summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="kr">function</span> <span class="nc">audio</span><span class="p">.</span><span class="nf">copy_and_paste</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span><span class="n">copy_start</span><span class="p">,</span><span class="n">copy_stop</span><span class="p">,</span><span class="n">paste_start</span><span class="p">,</span><span class="n">crossfade</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl">	<span class="kd">local</span> <span class="n">copy_length</span><span class="o">=</span><span class="n">copy_stop</span><span class="o">-</span><span class="n">copy_start</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl">	<span class="kr">if</span> <span class="n">copy_length</span><span class="o">==</span><span class="kc">nil</span> <span class="ow">or</span> <span class="n">copy_length</span><span class="o">&lt;</span><span class="mf">0.05</span> <span class="kr">then</span> 
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl">		<span class="kr">do</span> <span class="kr">return</span> <span class="n">fname</span> <span class="kr">end</span> 
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl">	<span class="kr">end</span>
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl">	<span class="kd">local</span> <span class="n">piece</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl">	<span class="kd">local</span> <span class="n">part1</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl">	<span class="kd">local</span> <span class="n">part2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">	<span class="kd">local</span> <span class="n">fname2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl">	<span class="kd">local</span> <span class="n">splice1</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl">	<span class="kd">local</span> <span class="n">e</span><span class="o">=</span><span class="n">crossfade</span> <span class="ow">or</span> <span class="mf">0.1</span> 
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl">	<span class="kd">local</span> <span class="n">l</span><span class="o">=</span><span class="mi">0</span> <span class="c1">-- no leeway</span>
</span></span><span class="line"><span class="ln" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim %f %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">piece</span><span class="p">,</span><span class="n">copy_start</span><span class="o">-</span><span class="n">e</span><span class="p">,</span><span class="n">copy_length</span><span class="o">+</span><span class="mi">2</span><span class="o">*</span><span class="n">e</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim 0 %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">part1</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">e</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">part2</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">copy_length</span><span class="o">-</span><span class="n">e</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s %s splice %f,%f,%f&#34;</span><span class="p">,</span><span class="n">part1</span><span class="p">,</span><span class="n">piece</span><span class="p">,</span><span class="n">splice1</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">e</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">l</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s %s splice %f,%f,%f&#34;</span><span class="p">,</span><span class="n">splice1</span><span class="p">,</span><span class="n">part2</span><span class="p">,</span><span class="n">fname2</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">copy_length</span><span class="o">+</span><span class="n">e</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">l</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;rm -f %s %s %s %s&#34;</span><span class="p">,</span><span class="n">piece</span><span class="p">,</span><span class="n">part1</span><span class="p">,</span><span class="n">part2</span><span class="p">,</span><span class="n">splice1</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a></span><span class="cl">	<span class="kr">return</span> <span class="n">fname2</span>
</span></span><span class="line"><span class="ln" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a></span><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div></details>
<p>(^ clicking that will expand the rest of the functions if you&rsquo;d like to see them.)</p>
<h2 id="lets-get-to-the-music">Lets get to the music</h2>
<p>I&rsquo;ll go through some of the pure functions I&rsquo;ve used for creating generative breakbeat music. First, here is the original audio loop that I will demonstrate with:</p>
<p><audio src="/img/sampswap/original.mp3" class="waveform"></audio></p>
<p>The original audio is great, but its short and can get repetitive so using a bunch of operations on it we can add repeats with variety.</p>
<h3 id="jumping">Jumping</h3>
<p>&ldquo;Jumping&rdquo; is what I call when you simply copy and paste one region to another region within the sample.</p>
<p><audio src="/img/sampswap/jump.mp3" class="waveform"></audio></p>
<p>The code for &ldquo;jumping&rdquo; is that <code>audio.copy_and_paste</code> function that I posted previously. Running that function a few times on the original sample yields something quite interesting.</p>
<h3 id="reversing">Reversing</h3>
<p>Reversing is what I call when you take a region and reverse it. Reversing an audio file is one of the simplest things and it can sound great when pasted back into the original audio.</p>
<p><audio src="/img/sampswap/reverse.mp3" class="waveform"></audio></p>
<p>The reversing function is really simple.</p>
<details><summary><code>audio.reverse(...)</code></summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a></span><span class="cl"><span class="kr">function</span> <span class="nc">audio</span><span class="p">.</span><span class="nf">reverse</span><span class="p">(</span><span class="n">fname</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a></span><span class="cl">  <span class="kd">local</span> <span class="n">fname2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3">3</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s reverse&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">fname2</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4">4</a></span><span class="cl">  <span class="kr">return</span> <span class="n">fname2</span>
</span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5">5</a></span><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div></details>
<p>But pasting it requires taking notice of the crossfade regions. I altered the <code>audio.copy_and_paste</code> function to allow pasting any piece of audio with crossfades. Without the crossfading it will inevitably produce &ldquo;clipping&rdquo; or &ldquo;popping&rdquo; sounds.</p>
<details><summary><code>audio.paste(...)</code></summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a></span><span class="cl"><span class="kr">function</span> <span class="nc">audio</span><span class="p">.</span><span class="nf">paste</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span><span class="n">piece</span><span class="p">,</span><span class="n">paste_start</span><span class="p">,</span><span class="n">crossfade</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a></span><span class="cl">	<span class="kd">local</span> <span class="n">copy_length</span><span class="o">=</span><span class="n">audio.length</span><span class="p">(</span><span class="n">piece</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a></span><span class="cl">  <span class="kr">if</span> <span class="n">copy_length</span><span class="o">==</span><span class="kc">nil</span> <span class="kr">then</span> 
</span></span><span class="line"><span class="ln" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a></span><span class="cl">    <span class="kr">do</span> <span class="kr">return</span> <span class="n">fname</span> <span class="kr">end</span> 
</span></span><span class="line"><span class="ln" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a></span><span class="cl">  <span class="kr">end</span>
</span></span><span class="line"><span class="ln" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a></span><span class="cl">	<span class="kd">local</span> <span class="n">part1</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a></span><span class="cl">	<span class="kd">local</span> <span class="n">part2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a></span><span class="cl">	<span class="kd">local</span> <span class="n">fname2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a></span><span class="cl">	<span class="kd">local</span> <span class="n">splice1</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a></span><span class="cl">	<span class="kd">local</span> <span class="n">e</span><span class="o">=</span><span class="n">crossfade</span> <span class="ow">or</span> <span class="mf">0.1</span> 
</span></span><span class="line"><span class="ln" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a></span><span class="cl">	<span class="kd">local</span> <span class="n">l</span><span class="o">=</span><span class="mi">0</span> <span class="c1">-- no leeway</span>
</span></span><span class="line"><span class="ln" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim 0 %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">part1</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">e</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">part2</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">copy_length</span><span class="o">-</span><span class="n">e</span><span class="o">*</span><span class="mi">3</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s %s splice %f,%f,%f&#34;</span><span class="p">,</span><span class="n">part1</span><span class="p">,</span><span class="n">piece</span><span class="p">,</span><span class="n">splice1</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">e</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">l</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a></span><span class="cl">	<span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s %s splice %f,%f,%f&#34;</span><span class="p">,</span><span class="n">splice1</span><span class="p">,</span><span class="n">part2</span><span class="p">,</span><span class="n">fname2</span><span class="p">,</span><span class="n">paste_start</span><span class="o">+</span><span class="n">copy_length</span><span class="o">+</span><span class="n">e</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">l</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-16"><a class="lnlinks" href="#hl-4-16">16</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;rm -f %s %s %s&#34;</span><span class="p">,</span><span class="n">part1</span><span class="p">,</span><span class="n">part2</span><span class="p">,</span><span class="n">splice1</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-4-17"><a class="lnlinks" href="#hl-4-17">17</a></span><span class="cl">	<span class="kr">return</span> <span class="n">fname2</span>
</span></span><span class="line"><span class="ln" id="hl-4-18"><a class="lnlinks" href="#hl-4-18">18</a></span><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div></details>
<p>Combining the <code>audio.reverse</code> and <code>audio.paste</code> on the original file creates the following:</p>
<h3 id="stutter">Stutter</h3>
<p>The stutter is my favorite effect. It is where you clip a piece of audio and then paste it many times at 1/16th note apart. At each new piece its fun to increase the volume or open up a filter.</p>
<p><audio src="/img/sampswap/stutter.mp3" class="waveform"></audio></p>
<details><summary><code>audio.stutter(...)</code></summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-5-1"><a class="lnlinks" href="#hl-5-1"> 1</a></span><span class="cl"><span class="kr">function</span> <span class="nc">audio</span><span class="p">.</span><span class="nf">stutter</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span><span class="n">stutter_length</span><span class="p">,</span><span class="n">pos_start</span><span class="p">,</span><span class="n">count</span><span class="p">,</span><span class="n">crossfade_piece</span><span class="p">,</span><span class="n">crossfade_stutter</span><span class="p">,</span><span class="n">gain_amt</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-5-2"><a class="lnlinks" href="#hl-5-2"> 2</a></span><span class="cl">  <span class="n">crossfade_piece</span><span class="o">=</span><span class="mf">0.1</span> <span class="ow">or</span> <span class="n">crossfade_piece</span>
</span></span><span class="line"><span class="ln" id="hl-5-3"><a class="lnlinks" href="#hl-5-3"> 3</a></span><span class="cl">  <span class="n">crossfade_stutter</span><span class="o">=</span><span class="mf">0.005</span> <span class="ow">or</span> <span class="n">crossfade_stutter</span>
</span></span><span class="line"><span class="ln" id="hl-5-4"><a class="lnlinks" href="#hl-5-4"> 4</a></span><span class="cl">  <span class="kd">local</span> <span class="n">partFirst</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-5-5"><a class="lnlinks" href="#hl-5-5"> 5</a></span><span class="cl">  <span class="kd">local</span> <span class="n">partMiddle</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-5-6"><a class="lnlinks" href="#hl-5-6"> 6</a></span><span class="cl">  <span class="kd">local</span> <span class="n">partLast</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-5-7"><a class="lnlinks" href="#hl-5-7"> 7</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim %f %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">partFirst</span><span class="p">,</span><span class="n">pos_start</span><span class="o">-</span><span class="n">crossfade_piece</span><span class="p">,</span><span class="n">stutter_length</span><span class="o">+</span><span class="n">crossfade_piece</span><span class="o">+</span><span class="n">crossfade_stutter</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-5-8"><a class="lnlinks" href="#hl-5-8"> 8</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim %f %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">partMiddle</span><span class="p">,</span><span class="n">pos_start</span><span class="o">-</span><span class="n">crossfade_stutter</span><span class="p">,</span><span class="n">stutter_length</span><span class="o">+</span><span class="n">crossfade_stutter</span><span class="o">+</span><span class="n">crossfade_stutter</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-5-9"><a class="lnlinks" href="#hl-5-9"> 9</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s trim %f %f&#34;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">partLast</span><span class="p">,</span><span class="n">pos_start</span><span class="o">-</span><span class="n">crossfade_stutter</span><span class="p">,</span><span class="n">stutter_length</span><span class="o">+</span><span class="n">crossfade_piece</span><span class="o">+</span><span class="n">crossfade_stutter</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-5-10"><a class="lnlinks" href="#hl-5-10">10</a></span><span class="cl">  <span class="n">gain_amt</span><span class="o">=</span><span class="n">gain_amt</span> <span class="ow">or</span> <span class="p">(</span><span class="n">count</span><span class="o">&gt;</span><span class="mi">8</span> <span class="ow">and</span> <span class="o">-</span><span class="mf">1.5</span> <span class="ow">or</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-5-11"><a class="lnlinks" href="#hl-5-11">11</a></span><span class="cl">  <span class="kr">for</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">count</span> <span class="kr">do</span> 
</span></span><span class="line"><span class="ln" id="hl-5-12"><a class="lnlinks" href="#hl-5-12">12</a></span><span class="cl">    <span class="kd">local</span> <span class="n">fnameNext</span><span class="o">=</span><span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-5-13"><a class="lnlinks" href="#hl-5-13">13</a></span><span class="cl">    <span class="kr">if</span> <span class="n">i</span><span class="o">==</span><span class="mi">1</span> <span class="kr">then</span> 
</span></span><span class="line"><span class="ln" id="hl-5-14"><a class="lnlinks" href="#hl-5-14">14</a></span><span class="cl">      <span class="n">fnameNext</span><span class="o">=</span><span class="n">audio.gain</span><span class="p">(</span><span class="n">partFirst</span><span class="p">,</span><span class="n">gain_amt</span><span class="o">*</span><span class="p">(</span><span class="n">count</span><span class="o">-</span><span class="n">i</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-5-15"><a class="lnlinks" href="#hl-5-15">15</a></span><span class="cl">    <span class="kr">else</span>
</span></span><span class="line"><span class="ln" id="hl-5-16"><a class="lnlinks" href="#hl-5-16">16</a></span><span class="cl">      <span class="n">fnameNext</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-5-17"><a class="lnlinks" href="#hl-5-17">17</a></span><span class="cl">    <span class="kd">local</span> <span class="n">fnameMid</span><span class="o">=</span><span class="n">i</span><span class="o">&lt;</span><span class="n">count</span> <span class="ow">and</span> <span class="n">partMiddle</span> <span class="ow">or</span> <span class="n">partLast</span> 
</span></span><span class="line"><span class="ln" id="hl-5-18"><a class="lnlinks" href="#hl-5-18">18</a></span><span class="cl">    <span class="kr">if</span> <span class="n">gain_amt</span><span class="o">~=</span><span class="mi">0</span> <span class="kr">then</span> 
</span></span><span class="line"><span class="ln" id="hl-5-19"><a class="lnlinks" href="#hl-5-19">19</a></span><span class="cl">      <span class="n">fnameMid</span><span class="o">=</span><span class="n">audio.gain</span><span class="p">(</span><span class="n">fnameMid</span><span class="p">,</span><span class="n">gain_amt</span><span class="o">*</span><span class="p">(</span><span class="n">count</span><span class="o">-</span><span class="n">i</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-5-20"><a class="lnlinks" href="#hl-5-20">20</a></span><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="ln" id="hl-5-21"><a class="lnlinks" href="#hl-5-21">21</a></span><span class="cl">      <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="s2">&#34;sox %s %s %s splice %f,%f,0&#34;</span><span class="p">,</span><span class="n">fname2</span><span class="p">,</span><span class="n">fnameMid</span><span class="p">,</span><span class="n">fnameNext</span><span class="p">,</span><span class="n">audio.length</span><span class="p">(</span><span class="n">fname2</span><span class="p">),</span><span class="n">crossfade_stutter</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-5-22"><a class="lnlinks" href="#hl-5-22">22</a></span><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="ln" id="hl-5-23"><a class="lnlinks" href="#hl-5-23">23</a></span><span class="cl">    <span class="n">fname2</span><span class="o">=</span><span class="n">fnameNext</span>
</span></span><span class="line"><span class="ln" id="hl-5-24"><a class="lnlinks" href="#hl-5-24">24</a></span><span class="cl">  <span class="kr">end</span>
</span></span><span class="line"><span class="ln" id="hl-5-25"><a class="lnlinks" href="#hl-5-25">25</a></span><span class="cl">  <span class="kr">return</span> <span class="n">fname2</span>
</span></span><span class="line"><span class="ln" id="hl-5-26"><a class="lnlinks" href="#hl-5-26">26</a></span><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div></details>
<p>This effect is also the most complicated because each little piece is crossfaded with each other little piece, but then all the pieces together must be pasted in with crossfades on either side. So I wrote the function to encompass the left, middle, and right sides so you can have shorter crossfades in the middle and longer crossfades to paste it into the original audio.</p>
<h3 id="reverse-reverb">Reverse reverb</h3>
<p>Reversing a reverb is another one of my favorite effects. This is one that is useful to have resampling (all these other effects could be done in realtime using a good sample player).</p>
<p><audio src="/img/sampswap/revrev.mp3" class="waveform"></audio></p>
<p>Reversing a reverb is pretty much how it sounds - take a slice and add reverb. Render the reverb ringing out and then reverse the whole thing.</p>
<p>While sox has a reverb built-in as well, I decided to use SuperCollider instead. SuperCollider can actually be run in &ldquo;non-realtime&rdquo; mode in which case you can use the SuperCollider toolkit and immediately (and pretty quickly) render audio with any of its building blocks.</p>
<details><summary><code>SuperCollider NRT server</code></summary>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
var oscScore;
var mainServer;
var nrtServer;
var serverOptions;
var scoreFn;

mainServer = Server(\sampswap_nrt, NetAddr(&#34;127.0.0.1&#34;, 47112));
serverOptions=ServerOptions.new.numOutputBusChannels_(2);
serverOptions.sampleRate=48000;
nrtServer = Server(\nrt, NetAddr(&#34;127.0.0.1&#34;, 47114), options:serverOptions);
SynthDef(&#34;lpf_rampup&#34;, {
    arg out=0,  dur=30, f1,f2,f3,f4;
    var duration=BufDur.ir(0);
    var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
    snd=LPF.ar(snd,XLine.kr(200,20000,duration));
    snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.005,dur-0.01,0.005]), doneAction:2);
    Out.ar(out, snd);
}).load(nrtServer);
SynthDef(&#34;lpf_rampdown&#34;, {
    arg out=0,  dur=30, f1,f2,f3,f4;
    var duration=BufDur.ir(0);
    var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
    snd=LPF.ar(snd,XLine.kr(20000,200,duration));
    snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.005,dur-0.01,0.005]), doneAction:2);
    Out.ar(out, snd);
}).load(nrtServer);
SynthDef(&#34;dec_ramp&#34;, {
    arg out=0,  dur=30, f1,f2,f3,f4;
    var duration=BufDur.ir(0);
    var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
    snd=SelectX.ar(Line.kr(0,1,duration/4),[snd,Decimator.ar(snd,8000,8)]);
    snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.005,dur-0.01,0.005]), doneAction:2);
    Out.ar(out, snd);
}).load(nrtServer);
SynthDef(&#34;dec&#34;, {
    arg out=0,  dur=30, f1,f2,f3,f4;
    var duration=BufDur.ir(0);
    var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
    snd=Decimator.ar(snd,8000,8);
    snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.005,dur-0.01,0.005]), doneAction:2);
    Out.ar(out, snd);
}).load(nrtServer);
SynthDef(&#34;reverberate&#34;, {
    arg out=0,  dur=30, f1,f2,f3,f4;
    var duration=BufDur.ir(0);
    var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
    snd=SelectX.ar(XLine.kr(0,1,duration/4),[snd,Greyhole.ar(snd* EnvGen.ar(Env.new([0, 1, 1, 0], [0.1,dur-0.2,0.1]), doneAction:2))]);
    snd=LeakDC.ar(snd);
    snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.1,dur-0.2,0.1]), doneAction:2);
    Out.ar(out, snd);
}).load(nrtServer);
SynthDef(&#34;filter_in_out&#34;, {
    arg out=0,  dur=30, f1,f2,f3,f4;
    var duration=BufDur.ir(0);
    var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
    snd = RLPF.ar(snd,
        LinExp.kr(EnvGen.kr(Env.new([0.1, 1, 1, 0.1], [f1,dur-f1-f2,f2])),0.1,1,100,20000),
        0.6);
    snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.005,dur-0.01,0.005]), doneAction:2);
    Out.ar(out, snd);
}).load(nrtServer);
SynthDef(&#34;tapedeck&#34;, {
	arg out=0,  dur=30,f1,f2,f3,f4,
	amp=0.9,tape_wet=0.95,tape_bias=0.9,saturation=0.9,drive=0.9,
	tape_oversample=1,mode=0,
	dist_wet=0.07,drivegain=0.5,dist_bias=0.5,lowgain=0.1,highgain=0.1,
	shelvingfreq=600,dist_oversample=1,
	hpf=60,hpfqr=0.6,
	lpf=18000,lpfqr=0.6;
	var duration=BufDur.ir(0);
	var snd = PlayBuf.ar(2,0,BufRateScale.kr(0));
	snd=snd*amp;
	snd=SelectX.ar(Lag.kr(tape_wet,1),[snd,AnalogTape.ar(snd,tape_bias,saturation,drive,tape_oversample,mode)]);	
	snd=SelectX.ar(Lag.kr(dist_wet/10,1),[snd,AnalogVintageDistortion.ar(snd,drivegain,dist_bias,lowgain,highgain,shelvingfreq,dist_oversample)]);				
	snd=RHPF.ar(snd,hpf,hpfqr);
	snd=RLPF.ar(snd,lpf,lpfqr);
	snd = snd * EnvGen.ar(Env.new([0, 1, 1, 0], [0.005,dur-0.01,0.005]), doneAction:2);
	Out.ar(out, snd);
}).load(nrtServer);

scoreFn={
    arg inFile,outFile,synthDefinition,durationScaling,oscCallbackPort,f1,f2,f3,f4;
    Buffer.read(mainServer,inFile,action:{
        arg buf;
        Routine {
            var buffer;
            var score;
            var duration=buf.duration*durationScaling;

            &#34;defining score&#34;.postln;
            score = [
                [0.0, [&#39;/s_new&#39;, synthDefinition, 1000, 0, 0, \dur,duration,\f1,f1,\f2,f2,\f3,f3,\f4,f4]],
                [0.0, [&#39;/b_allocRead&#39;, 0, inFile]],
                [duration, [\c_set, 0, 0]] // dummy to end
            ];

            &#34;recording score&#34;.postln;
            Score(score).recordNRT(
                outputFilePath: outFile,
                sampleRate: 48000,
                headerFormat: &#34;wav&#34;,
                sampleFormat: &#34;int24&#34;,
                options: nrtServer.options,
                duration: duration,
                action: {
                    Routine {
                        postln(&#34;done rendering: &#34; ++ outFile);
                        0.2.wait;
                        NetAddr.new(&#34;localhost&#34;,oscCallbackPort).sendMsg(&#34;/quit&#34;);
                    }.play;
                }
            );
        }.play;
    });
};
mainServer.waitForBoot({
    Routine {
        &#34;registring osc for score&#34;.postln;
        oscScore = OSCFunc({ arg msg, time, addr, recvPort;
            var inFile=msg[1].asString;
            var outFile=msg[2].asString;
            var synthDefinition=msg[3].asSymbol;
            var durationScaling=msg[4].asFloat;
            var oscCallbackPort=msg[5].asInteger;
            var f1=msg[6].asFloat;
            var f2=msg[7].asFloat;
            var f3=msg[8].asFloat;
            var f4=msg[9].asFloat;
            [msg, time, addr, recvPort].postln;
            scoreFn.value(inFile,outFile,synthDefinition,durationScaling,oscCallbackPort,f1,f2,f3,f4);
            &#34;finished&#34;.postln;
        }, &#39;/score&#39;,recvPort:47113);
        1.wait;
        &#34;writing ready file&#34;.postln;
        File.new(&#34;/tmp/nrt-scready&#34;, &#34;w&#34;);
        &#34;ready&#34;.postln;
    }.play;
});
)
</code></pre></details>
<p>I can still write a pure function to render the audio file, but I&rsquo;ll be using OSC to communicate with the NRT SuperCollider server to produce the result.</p>
<details><summary><code>audio.supercollider_effect(...)</code></summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-7-1"><a class="lnlinks" href="#hl-7-1"> 1</a></span><span class="cl"><span class="kr">function</span> <span class="nc">audio</span><span class="p">.</span><span class="nf">supercollider_effect</span><span class="p">(</span><span class="n">fname</span><span class="p">,</span><span class="n">effect</span><span class="p">,</span><span class="n">f1</span><span class="p">,</span><span class="n">f2</span><span class="p">,</span><span class="n">f3</span><span class="p">,</span><span class="n">f4</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-7-2"><a class="lnlinks" href="#hl-7-2"> 2</a></span><span class="cl">  <span class="kd">local</span> <span class="n">fname2</span><span class="o">=</span><span class="n">string.random_filename</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-7-3"><a class="lnlinks" href="#hl-7-3"> 3</a></span><span class="cl">  <span class="kd">local</span> <span class="n">durationScaling</span><span class="o">=</span><span class="mi">1</span> 
</span></span><span class="line"><span class="ln" id="hl-7-4"><a class="lnlinks" href="#hl-7-4"> 4</a></span><span class="cl">  <span class="kr">if</span> <span class="n">effect</span><span class="o">==</span><span class="s2">&#34;reverberate&#34;</span> <span class="kr">then</span> 
</span></span><span class="line"><span class="ln" id="hl-7-5"><a class="lnlinks" href="#hl-7-5"> 5</a></span><span class="cl">    <span class="n">durationScaling</span><span class="o">=</span><span class="mi">4</span>
</span></span><span class="line"><span class="ln" id="hl-7-6"><a class="lnlinks" href="#hl-7-6"> 6</a></span><span class="cl">  <span class="kr">end</span>
</span></span><span class="line"><span class="ln" id="hl-7-7"><a class="lnlinks" href="#hl-7-7"> 7</a></span><span class="cl">  <span class="n">f1</span><span class="o">=</span><span class="n">f1</span> <span class="ow">or</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-7-8"><a class="lnlinks" href="#hl-7-8"> 8</a></span><span class="cl">  <span class="n">f2</span><span class="o">=</span><span class="n">f2</span> <span class="ow">or</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-7-9"><a class="lnlinks" href="#hl-7-9"> 9</a></span><span class="cl">  <span class="n">f3</span><span class="o">=</span><span class="n">f3</span> <span class="ow">or</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-7-10"><a class="lnlinks" href="#hl-7-10">10</a></span><span class="cl">  <span class="n">f4</span><span class="o">=</span><span class="n">f4</span> <span class="ow">or</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-7-11"><a class="lnlinks" href="#hl-7-11">11</a></span><span class="cl">  <span class="n">os.cmd</span><span class="p">(</span><span class="n">string.format</span><span class="p">(</span><span class="n">SENDOSC</span><span class="o">..</span><span class="s1">&#39; --host 127.0.0.1 --addr &#34;/score&#34; --port 47113 --recv-port 47888 -s %s -s %s -s %s -s %s -s 47888 -s %f -s %f -s %f -s %f&#39;</span><span class="p">,</span><span class="n">fname</span><span class="p">,</span><span class="n">fname2</span><span class="p">,</span><span class="n">effect</span><span class="p">,</span><span class="n">durationScaling</span><span class="p">,</span><span class="n">f1</span><span class="p">,</span><span class="n">f2</span><span class="p">,</span><span class="n">f3</span><span class="p">,</span><span class="n">f4</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-7-12"><a class="lnlinks" href="#hl-7-12">12</a></span><span class="cl">  <span class="kr">return</span> <span class="n">fname2</span>
</span></span><span class="line"><span class="ln" id="hl-7-13"><a class="lnlinks" href="#hl-7-13">13</a></span><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div></details>
<h3 id="filter-inout">Filter in/out</h3>
<p>Speaking of SuperCollider effects&hellip;since I&rsquo;m already using the NRT server I opted to add some other fancy effects that sox can&rsquo;t quite do. For example - adding a filter to the beginning opening and a closing filter at the end.</p>
<p><audio src="/img/sampswap/filter.mp3" class="waveform"></audio></p>
<p>Its quite easy to add these effects using the same function above, just specifying which effect it is.</p>
<h2 id="all-together-now">All together now</h2>
<p>Each of those effects works by itself, but then their combination can be quite cool. By adding each effect with a given probability, at a random position you can quickly get a beat with variety and lots of movement.</p>
<p><audio src="/img/sampswap/render.mp3" class="waveform"></audio></p>
<h2 id="usage">Usage</h2>
<p>You can find the sources for all these files here: <a href="https://github.com/schollz/sampswap" target="_blank" >https://github.com/schollz/sampswap</a></p>
<p>There is also more usage information there.</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>Generations</title>
	    <image>https://schollz.com/img/a0359499364_16.jpg</image>
      <link>/music/generations/</link>
      <pubDate>Wed, 23 Feb 2022 08:00:46 -0700</pubDate>
      
      <guid>/music/generations/</guid>
      <description><![CDATA[<p>Earlier this month I released my 7th full-length album: <a href="https://infinitedigits.bandcamp.com/album/generations" target="_blank" >generations</a>. The creation of this album took only a few weeks, but the process building up to it took years of thinking about generative music and what I appreciate about it.</p>
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=2865516329/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/" seamless><a href="https://infinitedigits.bandcamp.com/album/generations">generations by infinite digits</a></iframe>
<h2 id="generative-music">Generative music</h2>
<p>Generative or &ldquo;automatic&rdquo; music is basically meaning that either composition or performance (or both) is created through some algorithm, recipe, or program. There are many approaches to generative or &ldquo;automatic&rdquo; music composition. Approaches range from <a href="https://www.thismusicvideodoesnotexist.com/" target="_blank" >deep-learning interpretations of music</a>, to <a href="https://teropa.info/musicmouse/" target="_blank" >clever mapping of musical space to physical coordinates</a>, to <a href="http://canonical.org/~kragen/bytebeat/" target="_blank" >musical emergence from mathematical formulas</a>, to <a href="https://flujoo.github.io/en/my-approach-to-automatic-musical-composition/" target="_blank" >developing frameworks of musical structure</a> (composition through decomposition).</p>
<p>I won&rsquo;t go through them all the possibilities here, suffice to say that there are lot and not all of them sound good (to me! sound is of course subjective, so all this is based on my own preference). To me, the deep-learning music examples are the most artifical sounding and I&rsquo;d prefer not to listen to them. To me, the methods that sound best are the ones that seek heuristics based on particular musical aesthetics (like the latter example that <a href="https://flujoo.github.io/en/my-approach-to-automatic-musical-composition/" target="_blank" >reconstructs classical music</a> from core paradigms).</p>
<h2 id="how-to-make-music-from-a-toy-synthesizer">How to make music from a toy synthesizer</h2>
<p>I first started thinking about generative music when I was trying to make music from a cheap toy synthesizer. I had bought a Korg Monotron Delay and I wanted to make a <em>good</em> sounding album from it but was challenged because it only outputs one note at a time. I decided to make a looper so I could loop notes atop each other and then tried to figure out methods for layering the notes in interesting ways. After <a href="https://schollz.com/raspberrypi/monotron/" target="_blank" >hacking the toy synth</a> I created <a href="https://schollz.com/blog/oooooo/" target="_blank" >a little looper</a> that would loop the synthesizer onto six loops, with four notes per loop.</p>
<p>So what should the notes be? I <a href="https://github.com/schollz/miti" target="_blank" >wrote a little</a> sequencer and started writing lists of notes. I tried ordering the notes in basic intervals (root, 3rd, tonic, etc) and I tried random ordering. Eventually I settled on this type of &ldquo;minimal transposition&rdquo; scheme where you order notes by grouping close notes together.  For example: first suppose you chose four chords: Am, F, C, G. acrostic will first determine the notes for <em>each chord in a separate column</em>:</p>
<pre tabindex="0"><code>Am  F   C   G  
---------------
A   F   C   G
C   A   E   B
E   C   G   D
</code></pre><p>Then I would  rearrange the notes of each chord in each column so that there are minimal changes between different columns.</p>
<pre tabindex="0"><code>Am  F   C   G  
---------------
C   C   C   D
A   A   G   G
E   F   E   B
</code></pre><p>Then I would read off, row by row, the notes where each row would be one full loop in my looper script. This is not a new techniique, in fact its a very simple form of <a href="https://en.wikipedia.org/wiki/Voice_leading" target="_blank" >voice leading</a>. Eventually these long lists of notes where turned into songs when I put everything together. For example, this <a href="https://github.com/schollz/album-at-the-place/blob/master/songs/everything.miti" target="_blank" >list of notes</a> became this song:</p>
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=3395815116/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/track=1901594465/transparent=true/" seamless><a href="https://infinitedigits.bandcamp.com/album/at-the-place">at the place by infinite digits</a></iframe>
<br>
<h2 id="a-realization-about-articulation">A realization about articulation</h2>
<p>I recorded the songs with that toy synthsizer two years ago. Since then I&rsquo;ve been thinking about it - how simple the process was for generating the music and how much I enjoyed the result. Part of the result, though, was not in the pitches themselves but in their <em>articulation</em> - i.e. how loud one series of notes might be.</p>
<p>So I went ahead to write another program. One that would <em>automatically</em> create the matrix of notes and record each row of the matrix, one note at a time, into different loops. I called this program &ldquo;acrostic&rdquo; since the note matrix has meaning in its columns (chords) and in its rows (melodies). The key though, was to provide each loop with a specific articulation - basically it would allow each one to get louder and softer randomly over time. This allows different meldies to come in and out of focus, which instantly gives songs their &ldquo;movement&rdquo;.</p>
<p>For example, here is a track from my latest album &ldquo;generations&rdquo; where the volume of each track is visualized by the vertical position of one of the filled circles:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/663740623" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>To me, this sounded great. Not only that, using the same four chords would give greatly different results which is an important part of generative music (to me). The randomness in the process was enough to generative varied performances but not too much to produce noise.</p>
<h2 id="the-recipe-for-generative-music">The recipe for generative music</h2>
<p>So after this little discovery I had a basic recipe for generative ambient music.:</p>
<ol>
<li>Choose chords.</li>
<li>Create matrix of notes from chords and rearrange rows in each column to minimize distance between columns.</li>
<li>Render each row, one note at a time, with some instrument/synthesizer into a looper that stores each row as its own track.</li>
<li>Add articulation to each loop track by modulating its volume with a simple LFO.</li>
</ol>
<p>I used this to set about recording dozens and dozens of songs for this album. It was easy to record - in my <a href="https://github.com/schollz/acrostic" target="_blank" >acrostic</a> software, I basically just put the chords I wanted (as input) and then let it go while capturing the output. Afterwards I did a little bit of mastering (normalizing levels, taking off some of the low end) and released it as &ldquo;generations&rdquo;. There is one of the title tracks with an animation I made visualizing the articulation of tracks:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/672485075" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<details><summary>(sidenote: this is the code for the art, generated using Processing)</summary>
<script src="https://gist.github.com/schollz/4b2bd31ac2abca915a76e0c25a185a7e.js"></script>
</details>
<br>
<p>Each track is made the same way - following those four steps above - and using the same sound source (a synthesizer called the Make Noise Strega). But you could easily do the same thing with a toy synthesizer, or any instrument really. There are some other little tricks - I introduced a little variation by adding in random melodies that where interpolations between neighboring columns in that matrix. But that&rsquo;s something for another story!</p>
<h2 id="my-code-referenced">My code referenced</h2>
<p>Here is a list of repositories I wrote that were mentioned above:</p>
<ul>
<li><a href="https://github.com/schollz/miti" target="_blank" >miti</a> - a text-based music sequencer</li>
<li><a href="https://github.com/schollz/oooooo" target="_blank" >oooooo</a> - a six-loop sampler</li>
<li><a href="https://github.com/schollz/acrostic" target="_blank" >acrostic</a> - a chord sequencer and sampler</li>
</ul>
]]></description>
    </item>
    
    <item>
	    <title>Internet radio</title>
	    <image>https://schollz.com/img/online.jpg</image>
      <link>/tinker/radio/</link>
      <pubDate>Wed, 12 Jan 2022 08:00:46 -0700</pubDate>
      
      <guid>/tinker/radio/</guid>
      <description><![CDATA[<p>For several years I&rsquo;ve been interested in <em>easily</em> making an internet broadcast of live audio or recorded audio without using social media apps (i.e. Instagram/Youtube/Twitch/etc). The most problematic for me thing about broadcasting audio is port forwarding (I&rsquo;m often on devices that don&rsquo;t have access to routers). To solve this problem I wrote a simple server that runs in the cloud which can handle any number of realtime broadcasts uploaded using a simple <code>curl</code> command. This allows you to essentially <a href="#method-1-of-2-broadcasting-with-ffmpeg-one-line-of-code" >broadcast radio feeds with a single line</a>.</p>
<h2 id="publicizing-internet-streams-without-port-forwarding">Publicizing internet streams without port forwarding</h2>
<p>For me, the hardest part about making an internet radio is opening it up to the public internet. I don&rsquo;t have access to port-forwarding in many scenarios where I want to broadcast (i.e. broadcasting from friends places, or from a <a href="https://monome.org/norns" target="_blank" >music device</a>). My solution to the problem of broadcasting audio without port-forwarding was to create a free and public &ldquo;dummy&rdquo; server that handles any incoming traffic and forwards to any number of connected clients. This server keeps track of connected clients and routes incoming packets to them, otherwise discarding the packets.</p>
<p>You could then simply use a common utility like <code>curl</code> to stream data to this server to those peers. This is essentially what Twitch/Youtube/Instagram do, but I designed a server without any social aspects, its just a dumb URL endpoint that can consume and produce audio streams (or any stream).</p>
<p>The server itself lives at <a href="https://broadcast.schollz.com" target="_blank" >broadcast.schollz.com</a>. The code is open-source and available on Github at <a href="https://github.com/schollz/broadcast-server/" target="_blank" >schollz/broadcast-server</a>. The code is less than 400 lines, and exceedingly simple. You <code>POST</code> data streams to any endpoint you want, and processing <code>GET</code> requests to that endpoint just return the data in that stream. There are special flags if you want to advertise or archive your stream.</p>
<p>Now using this really dumb server which just lives on a DigitalOcean droplet you can easily create internet streams from anywhere, with anything, using a variety of methods.</p>
<h3 id="live-station-using-ffmpeg">Live station using <code>ffmpeg</code></h3>
<p>To make a live internet radio from the input of your computer, you can do it in one line (albeit long line):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl">ffmpeg -f alsa -i hw:0 -f mp3 - <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl"><span class="se"></span>    curl -s -k -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T - <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a></span><span class="cl"><span class="se"></span>    <span class="s2">&#34;https://broadcast.schollz.com/YOURSTATIONNAME.mp3?stream=true&amp;advertise=true&#34;</span>
</span></span></code></pre></div><p>That one-liner takes input from your default input <code>hw:0</code> and streams it into a <code>POST</code> request to the dummy server making the stream available at <code>broadcast.schollz.com/YOURSTATIONNAME.mp3</code>. Make sure you wear headphones if you create a stream on the same computer you are listening to the stream (because feedback)!</p>
<h3 id="live-station-using-vlc">Live station using <code>vlc</code></h3>
<p><strong>Linux:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">vlc -I dummy alsa://plughw:0,0 --sout<span class="o">=</span><span class="s1">&#39;#transcode{vcodec=none,acodec=mp3,ab=256,channels=2,samplerate=44100,scodec=none}:standard{access=file,mux=mp3,dst=-}&#39;</span> --no-sout-all --sout-keep <span class="p">|</span> curl -k -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T -  <span class="s1">&#39;https://broadcast.schollz.com/YOURSTATIONNAME.mp3?stream=true&amp;advertise=true&#39;</span>
</span></span></code></pre></div><p><strong>Mac (requires VLC 2.0.0 or later):</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl">vlc -I dummy -vvv qtsound:// --sout<span class="o">=</span><span class="s1">&#39;#transcode{vcodec=none,acodec=mp3,ab=256,channels=2,samplerate=44100,scodec=none}:standard{access=file,mux=mp3,dst=-}&#39;</span> --no-sout-all --sout-keep <span class="p">|</span> curl -k -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T -  <span class="s1">&#39;https://broadcast.schollz.com/YOURSTATIONNAME.mp3?stream=true&amp;advertise=true&#39;</span>
</span></span></code></pre></div><h3 id="playlist-station-using-ffmpeg">Playlist station using <code>ffmpeg</code></h3>
<p>Creating an internet radio station from a music playlist is also easily done. You need one extra common utility: <code>cstream</code> which will make sure the stream is sent in realtime, so its not loaded into memory. Here&rsquo;s a one liner that will take any folder and create a randomly shuffled playlist and stream it to the station:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a></span><span class="cl">find ~/Music/ <span class="p">|</span> grep <span class="s1">&#39;wav\|mp3\|flac&#39;</span> <span class="p">|</span> shuf <span class="p">|</span> sed -e <span class="s2">&#34;s/^/file &#39;/&#34;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a></span><span class="cl"><span class="se"></span>    sed -e <span class="s2">&#34;s/</span>$<span class="s2">/&#39;/&#34;</span> &gt; /tmp/playlist.txt <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3">3</a></span><span class="cl"><span class="se"></span>    ffmpeg -f concat -safe <span class="m">0</span> -i /tmp/playlist.txt -f mp3 -ar <span class="m">44100</span> -b:a 256k - <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4">4</a></span><span class="cl"><span class="se"></span>    cstream -t 32k <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5">5</a></span><span class="cl"><span class="se"></span>    curl -s -k -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T -  <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-3-6"><a class="lnlinks" href="#hl-3-6">6</a></span><span class="cl"><span class="se"></span>    <span class="s2">&#34;https://broadcast.schollz.com/YOURSTATIONNAME.mp3?stream=true&amp;advertise=true&#34;</span>
</span></span></code></pre></div><p>In this case we are streaming at 256 kbps, which is 32 KB/s, so <code>cstream</code> is set to throttle the data to that rate so it transfers in &ldquo;realtime&rdquo;.</p>
<h3 id="play-file-using-vlc">Play file using <code>vlc</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">1</a></span><span class="cl">vlc -I dummy ANYFILE <span class="s1">&#39;:sout=#transcode{acodec=mp3,ab=52,channels=2}:standard{access=file,mux=mp3,dst=-}&#39;</span>  <span class="p">|</span> cstream -t 16k <span class="p">|</span> curl -k -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T -  <span class="s1">&#39;https://broadcast.schollz.com/YOURSTATIONNAME.mp3?stream=true&amp;advertise=true&#39;</span>
</span></span></code></pre></div><h2 id="method-2-of-2-broadcasting-using-icecast2-and-darkice">Method 2 of 2: Broadcasting using <code>icecast2</code> and <code>darkice</code></h2>
<p>The <code>icecast2</code> and <code>darkice</code> utilities are installed via <code>apt-get</code> and really powerful when it comes to streaming. I won&rsquo;t go into much detail with these two, because there are a lot of guides for it at other places. Here is a <a href="https://maker.pro/raspberry-pi/projects/how-to-build-an-internet-radio-station-with-raspberry-pi-darkice-and-icecast" target="_blank" >great guide for doing live radio</a> on a Raspberry Pi.</p>
<p>If you are on Linux, you can also make your entire computer audio part of the stream (i.e. your browser, VLC, etc.). You can do this with JACK. Simply install JACK:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-5-1"><a class="lnlinks" href="#hl-5-1">1</a></span><span class="cl">sudo apt install jackd pulseaudio-module-jack 
</span></span></code></pre></div><p>Then start JACK and load the sink/source for pulse audio:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-6-1"><a class="lnlinks" href="#hl-6-1">1</a></span><span class="cl">jackd -R -dalsa -dhw:0,0 <span class="p">&amp;</span>
</span></span><span class="line"><span class="ln" id="hl-6-2"><a class="lnlinks" href="#hl-6-2">2</a></span><span class="cl">pactl load-module module-jack-sink <span class="nv">channels</span><span class="o">=</span><span class="m">2</span>
</span></span><span class="line"><span class="ln" id="hl-6-3"><a class="lnlinks" href="#hl-6-3">3</a></span><span class="cl">pactl load-module module-jack-source <span class="nv">channels</span><span class="o">=</span><span class="m">2</span>
</span></span><span class="line"><span class="ln" id="hl-6-4"><a class="lnlinks" href="#hl-6-4">4</a></span><span class="cl">pacmd set-default-sink jack_out
</span></span><span class="line"><span class="ln" id="hl-6-5"><a class="lnlinks" href="#hl-6-5">5</a></span><span class="cl">pacmd set-default-source jack_in
</span></span></code></pre></div><p>Now start <code>icecast2</code> and <code>darkice</code> with a JACK configuration.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-7-1"><a class="lnlinks" href="#hl-7-1">1</a></span><span class="cl">wget https://raw.githubusercontent.com/schollz/broadcast/main/icecast.xml
</span></span><span class="line"><span class="ln" id="hl-7-2"><a class="lnlinks" href="#hl-7-2">2</a></span><span class="cl">wget https://raw.githubusercontent.com/schollz/broadcast/main/darkice.cfg
</span></span><span class="line"><span class="ln" id="hl-7-3"><a class="lnlinks" href="#hl-7-3">3</a></span><span class="cl">icecast2 -c icecast.xml <span class="p">&amp;</span> 
</span></span><span class="line"><span class="ln" id="hl-7-4"><a class="lnlinks" href="#hl-7-4">4</a></span><span class="cl">darkice -c darkice.cfg
</span></span></code></pre></div><p>Now simply connect <code>darkice</code> into JACK!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-8-1"><a class="lnlinks" href="#hl-8-1">1</a></span><span class="cl">jack_connect <span class="s1">&#39;PulseAudio JACK Sink:front-right&#39;</span> darkice:right
</span></span><span class="line"><span class="ln" id="hl-8-2"><a class="lnlinks" href="#hl-8-2">2</a></span><span class="cl">jack_connect <span class="s1">&#39;PulseAudio JACK Sink:front-left&#39;</span> darkice:left
</span></span></code></pre></div><h3 id="making-a-darkice-stream-public">Making a <code>darkice</code> stream public</h3>
<p>Once you have your <code>darkice</code>/<code>icecast2</code> stream, you can make it public by port forwarding. There are lots of guides for that. But another way you could do it is using the dummy broadcast server I mention above. Simply pipe the output from <code>darkice</code> and <code>POST</code> it to the server!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-9-1"><a class="lnlinks" href="#hl-9-1">1</a></span><span class="cl">curl http://localhost:8000/radio.mp3 <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-9-2"><a class="lnlinks" href="#hl-9-2">2</a></span><span class="cl"><span class="se"></span>    curl -H <span class="s2">&#34;Transfer-Encoding: chunked&#34;</span> -X POST -T -  <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-9-3"><a class="lnlinks" href="#hl-9-3">3</a></span><span class="cl"><span class="se"></span>    <span class="s1">&#39;https://broadcast.schollz.com/YOURSTATIONNAME.mp3?stream=true&#39;</span>
</span></span></code></pre></div><p>That&rsquo;s it! Now your stream will be available at <code>broadcast.schollz.com/YOURSTATIONNAME.mp3</code>.</p>
<h2 id="integration-with-live-music-tools">Integration with live music tools</h2>
<p>I do <a href="/tags/norns/" >a lot of projects</a> with the pi-based sound computer called &ldquo;<a href="https://monome.org/norns/" target="_blank" >norns</a>&rdquo; which lets you define its behavior with scripts and DSP and basically functions as a sample-cutter, polysynth, drum machine, drone box, granulator, etc.</p>
<p>The norns music tool is based in Linux, so the above methods are just as applicable. This means that the norns can be turned into a broadcast station, while retaining all the functionality as a DSP device! Here&rsquo;s <a href="https://github.com/schollz/broadcast" target="_blank" >my little script</a> that converts live audio into a broadcast, directly from your house or live music venue (as long as you have WiFi).</p>
<p><img src="/img/online.jpg" alt="norns broadcasting internet radio"></p>
<p>The above norns script uses the <code>darkice</code>/<code>icecast2</code> because it is based around JACK, but it works beautifully despite running on a Raspberry Pi 3+ and simultaneously running a sample-cutter and SuperCollider synthesizers.</p>
]]></description>
    </item>
    
    <item>
	    <title>pianos.pub</title>
	    <image>https://schollz.com/img/pianos.pub.jpg</image>
      <link>/tinker/pianos-for-travelers/</link>
      <pubDate>Sun, 19 Dec 2021 07:06:29 -0800</pubDate>
      
      <guid>/tinker/pianos-for-travelers/</guid>
      <description><![CDATA[<p>TLDR: the website that resulted from this is <a href="https://pianos.pub" target="_blank" >pianos.pub</a>.</p>
<p>Pianists are lucky. The piano is an instrument that - unlike trumpets, saxophones, etc. - sometimes is available to play <em>for free</em>. These <em>free-to-play</em> pianos are called &ldquo;public pianos&rdquo; (or &ldquo;street pianos&rdquo;) which often appear in airports, train stations and parks during good weather.</p>
<p>Pianists know that these public pianos exist, no one knows where most of these pianos are and as such they exist in the world as a sort of magical entity whose secret location needs to be discovered. I am attempting to discover the location of all of them, and make a map of every piano in the world.</p>
<p>The result of this attempt is a website: <a href="https://pianos.pub" target="_blank" >Pianos for Travelers</a>. This blog post tells about how the website was made.</p>
<h2 id="getting-the-data">Getting the data</h2>
<p>I estimate that there are thousands of public pianos in the world and at least one in every major city in the world. Obviously I cannot go to every city in the world to find every piano, so I am attempting to use the internet to find their locations.</p>
<p>There is a handful of websites that compile piano lists including <a href="https://airport-pianos.fandom.com/wiki/Airport_pianos_Wiki" target="_blank" >a slowly updated wiki</a> and <a href="https://www.pianoplayersclub.com/" target="_blank" >an out-of-date map</a>, forums, subreddits and etc. I collected hundreds of piano locations going through these types of websites and manually cataloging their coordinates in a file.</p>
<p>After going through every website I could find, I only had a few hundred pianos - there were still over a thousand out there. Luckily, I realized that social media actually has tracked locations of people using the hashtags <code>#publicpiano</code> and I was able to use APIs to collect their coordinates as well. This netted hundreds of more pianos.</p>
<p>At this point, until I find another source of pianos, I will be bootstrapping myself and heeding user input to gather the locations of more pianos. This is usually where the other websites failed, and mine might too, so I&rsquo;m still looking for alternatives to this practice.</p>
<h2 id="creating-the-website">Creating the website</h2>
<p>I designed the <em><a href="https://pianos.pub" target="_blank" >Pianos for Travelers</a></em> site with my good friend. We used a very simple stack - Go std-lib http router, with <a href="https://www.postgresql.org/" target="_blank" >Postgres 12</a> backend, <a href="https://jquery.com/" target="_blank" >JQuery v3</a> frontend, and using <a href="https://tachyons.io/" target="_blank" >Tachyons</a> for CSS. (I actually disagree that Postgres is very simple, but it is very powerful when it comes to GIS information).</p>
<p><img src="/img/pianostravel.png" alt="Pianos for Travelers website"></p>
<p>The Go language constantly impresseses me with how fast we can move into production - for example we needed a CAPTCHA and found an <a href="https://github.com/dchest/captcha" target="_blank" >amazing package by dchest</a> that was basically a dropin into our std-lib web server. Same thing happened when we switched to Websockets. We found  the entire site took less than two weeks to build (and we only spent our free time on it).</p>
<h2 id="shipping-the-website">Shipping the website</h2>
<p>The website, <em><a href="https://pianos.pub" target="_blank" >Pianos for Travelers</a></em>, is live now. We posted about it on sites that might have interest in places with pianos (Piano forums, subreddits) and it gained a little traction. It is up for perpetuity now, though so I&rsquo;m hoping it provides some useful information to the people that find it.</p>
]]></description>
    </item>
    
    <item>
	    <title>CZ-101</title>
	    <image>https://schollz.com/img/casiopatent.png</image>
      <link>/tinker/phasedistortion/</link>
      <pubDate>Sat, 21 Aug 2021 07:50:07 -0700</pubDate>
      
      <guid>/tinker/phasedistortion/</guid>
      <description><![CDATA[<p>There is a classic synthesizer from the 80&rsquo;s, the 1984 Casio CZ-101. It has a special sound directly related to an innovative technique used in the creation.</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/c9iIHNVh36A" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

<br>
<p>One of the things that gave it a distinctive sound was the phase distortion synthesis. Phase distortion synthesis is similar to phase modulation and was used to simulate a resonant filter. This is super useful to be able to make iconic filter sweeps.</p>
<p>Basically it was done using a digital hard sync, by multiplying a hard synced signal against the inverted counter to level out the final signal. It is well described in <a href="https://patents.google.com/patent/US4658691" target="_blank" >Casio Computer Co Ltd patent</a> filed in 1983:</p>
<p><img src="/img/casiopatent.png" alt="Figure 19 from the USPTO CZ-series patent application"></p>
<p>It turns out it&rsquo;s quite easy to generate this plot, pretty much exactly, just using SuperCollider code.</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
{
	var freqBase=100;
	var freqRes=130;
	var pdbase=Impulse.ar(freqBase);
	var pd=Phasor.ar(pdbase,2*pi*freqBase/s.sampleRate,0,2*pi);
	var pdres=Phasor.ar(pdbase,2*pi*freqRes/s.sampleRate,0,2*pi);
	var pdi=LinLin.ar((2*pi-pd).max(0),0,2*pi,0,1);
	[pd/(2*pi),pdres/(2*pi),SinOsc.ar(0,pdres),pdi,Lag.ar(SinOsc.ar(0,pdres)*pdi,1/freqBase)];
}.plot(0.011)
)
</code></pre><p>The code above results in a SuperCollider plot that looks eerily similar to the patent figure.</p>
<p><img src="/img/casiosc.png" alt="SuperCollider recreation of the patent figure"></p>
<p>Now we can take that code and put it into a synth, and we have a re-creation of one small part of the Casio keyboard from 1984.</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
Routine {
	SynthDef(&#34;casioish&#34;,{
		arg freq=220, amp=0.5;
		var freqBase=freq;
		var freqRes=SinOsc.kr(Rand(0,0.2),0).range(freqBase/2,freqBase*2);
		var pdbase=Impulse.ar(freqBase);
		var pd=Phasor.ar(pdbase,2*pi*freqBase/s.sampleRate,0,2pi);
		var pdres=Phasor.ar(pdbase,2*pi*freqRes/s.sampleRate,0,2pi);
		var pdi=LinLin.ar((2pi-pd).max(0),0,2pi,0,1);
		var snd=Lag.ar(SinOsc.ar(0,pdres)*pdi,1/freqBase).dup;
		snd=Splay.ar(snd);
		snd=snd*EnvGen.ar(Env.perc(0.005,10));
		Out.ar(0,snd*amp);
	}).add;
	s.sync;

	x = Synth(&#34;casioish&#34;,[\freq,60.midicps]);
	y = Synth(&#34;casioish&#34;,[\freq,62.midicps]);
	z = Synth(&#34;casioish&#34;,[\freq,65.midicps]);
	w = Synth(&#34;casioish&#34;,[\freq,60.midicps/2,\amp,1.0]);
}.play
)
</code></pre><p>And here&rsquo;s an example of the resulting sound:</p>
<p><audio src="/img/casioexample.mp3" type="audio/mp3" class="waveform" controls></audio></p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>PO-32 hack</title>
	    <image>https://schollz.com/img/tinyhacks/po32.png</image>
      <link>/tinker/po32/</link>
      <pubDate>Fri, 20 Aug 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/po32/</guid>
      <description><![CDATA[<p>Teenage Engineering created one of the affordable, yet powerful, drum machines out there with the Pocket Operator PO-32. It features a wide array of adjustable drum sounds (and pitches) and a calculator-esque sequencer with ready-made effects. The Pocket Operators are made and distributed in this slimmed-down no-case design, which invites hacking. In fact there was a <a href="https://teenage.engineering/pop-report" target="_blank" >Teenage Engineering sponsored hacking competition</a> where a number of clever and silly additions to the Pocket Operators were introduced.</p>
<p>The <em>tiny hack</em> I want to introduce is so simple but adds a great deal of functionality to the PO-32. The PO-32 drum sequencer has a unique feature where the four columns are designated as different channels and while playing, any key in will mute that corresponding channel. This is extremely useful in case you make a drum pattern with four instruments and want each one to come in one at a time. To use it though, you physically have to hold down one key for each channel you want to mute.</p>
<h2 id="what-does-this-tiny-hack-do">What does this <em>tiny hack</em> do?</h2>
<p>This <em>tiny hack</em> bypasses your fingers and uses switches. The switches are placed at the bottom, out of the way of most of the keys, and simply allow you to activate the &ldquo;mute channel&rdquo; feature without holding down any keys. Since its placed in parallel of the button, there is no other circuitry needed. Here&rsquo;s a demo of it:</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/590429155" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<h2 id="why-do-this-tiny-hack">Why do this <em>tiny hack</em>?</h2>
<p>This <em>tiny hack</em> is useful if you use the PO-32 and find yourself making a pattern for individual instruments: i.e. one pattern with bass drum, another pattern with the same bass drum but adding a snare, etc. This tiny hack lets you build a single pattern with all the instruments and you can toggle them on/off simply by flipping a switch!</p>
<h2 id="how-do-i-do-this-tiny-hack">How do I do this <em>tiny hack</em>?</h2>
<p>One note of caution: I have confirmation from Teenage Engineering that this will void your warranty.</p>
<p>This <em>tiny hack</em> is tiny indeed. All you need are four switches, some wire, and a soldering iron (and a PO-32 of course).</p>
<p>First connect the switches to two wires and cut them so that they are similar length (will make it easier for next step).</p>
<p>Then heat up the bottom of one of the legs of the key on the PO-32 and use solder to solder the wire of the switch to it. Repeat for each key that you want to toggle!</p>
<p>One note on use: make sure you turn <em>off</em> the switches when you are done playing with the PO-32 otherwise it will continue to remain on until the battery expires.</p>
]]></description>
    </item>
    
    <item>
	    <title>Live coding</title>
	    <image>https://schollz.com/img/internorns.jpg</image>
      <link>/tinker/internorns/</link>
      <pubDate>Tue, 06 Jul 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/internorns/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/570686702" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>I made this specifically for the <a href="https://llllllll.co/t/45273" target="_blank" >upcoming flash crash</a> performance. this script is a cross between a <a href="https://llllllll.co/t/live-coding/5032" target="_blank" >live coding environment</a> and a <a href="https://llllllll.co/t/trackers/38551" target="_blank" >tracker</a> - its sorta a tracker you sequence with code. the code you can sequence is any norns lua code. using code you can access any of the standard norns features (midi, osc, crow), and I&rsquo;ve also added interfaces to &ldquo;lite&rdquo; versions of several of my own scripts (like <a href="https://llllllll.co/t/oooooo" target="_blank" ><em>oooooo</em></a>, <a href="https://llllllll.co/t/supertonic/" target="_blank" >supertonic</a>, <a href="https://llllllll.co/t/amen/" target="_blank" >amen</a>, and <a href="https://llllllll.co/t/mx-samples/" target="_blank" >mx.samples</a>). some basic features of internorns:</p>
<ul>
<li>sequence pitches with text, in chords (e.g. <code>Cm7/Eb</code>) or notes (e.g. <code>a4 eb3</code>)</li>
<li>sequence midi devices and add cc lfos</li>
<li>sequence crow with pitches, automatically generating voltage/envelope</li>
<li>play/rec into three stereo loops via softcut</li>
<li>built-in drum synth that can be sequenced/modulated in realtime</li>
<li>built-in sample player that can be quantized</li>
<li>sequence notes/chords from any instrument in <a href="https://llllllll.co/t/mx-samples/" target="_blank" >mx.samples</a></li>
<li>special tape stop / start global fx</li>
</ul>
<p>internorns works quite simply: it runs an internal sequencer that runs code. there is an internal clock that makes 4 steps per beat, and 4 beats per measure. at each step it checks to see if there is code to run in that step in the current measure and attempts to run it. code can be added to steps using the built-in functions.</p>
<p>See <a href="https://github.com/schollz/internorns/blob/main/data/getting-started.lua" target="_blank" ><code>data/getting-started.lua</code></a> to get started and preview how it works. the process is music.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li>computer</li>
<li>norns</li>
<li>midi device (optional)</li>
<li>crow (optional)</li>
</ul>
<h2 id="documentation">Documentation</h2>
<p>start the internorns script on norns.</p>
<p>now choose an editor to live-code, I suggest usig either maiden (in the browser), visual studio code, or vim. instructions for each are below.</p>
<details><summary><strong>maiden</strong></summary>
<p>open up a webbrowser to <a href="norns.local/maiden/#edit/dust/data/internorns/getting-started.lua" >http://norns.local/maiden/#edit/dust/data/internorns/getting-started.lua</a>.</p>
<p>you can select any code and press <kbd>ctl</kbd>+<kbd>enter</kbd> to send that code to the norns.</p>
<p><em>note:</em> requires latest version of maiden.</p>
</details>
<details><summary><strong>visual studio code</strong></summary>
<p><a href="https://code.visualstudio.com/" target="_blank" >download visual studio code</a> and then install <a href="https://llllllll.co/t/norns-repl-vscode-extension/41382" target="_blank" >the Norns REPL extension</a>. use software like <a href="https://www.nsoftware.com/sftp/drive/" target="_blank" >sftp drive</a> to mount your norns on your computer. then you can directly edit <code>~/dust/data/internorns/getting-started.lua</code>.</p>
<p>press <kbd>ctl</kbd>+<kbd>enter</kbd> to send the current line to the norns.</p>
</details>
<details><summary><strong>vim</strong></summary>
<p>lines from a norns script can be quickly and easily run using vim.</p>
<p>to use with vim, first download <code>wscat</code> - a utility for piping commands to the maiden websocket server.</p>
<pre tabindex="0"><code>wget https://github.com/schollz/wscat/releases/download/binaries/wscat
chmod +x wscat
sudo mv wscat /usr/local/bin/
</code></pre><p>then you can edit your <code>.vimrc</code> file to include these lines which will automatically run
the current selected line when you press <kbd>ctl</kbd>+<kbd>c</kbd>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vim" data-lang="vim"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="k">set</span> <span class="nb">underline</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl"><span class="nx">nnoremap</span> <span class="p">&lt;</span><span class="nx">C</span><span class="p">-</span><span class="nx">c</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nx">esc</span><span class="p">&gt;</span>:<span class="nx">silent</span>.<span class="nx">w</span> <span class="p">!</span><span class="nx">wscat</span><span class="p">&lt;</span><span class="nx">enter</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl"><span class="nx">inoremap</span> <span class="p">&lt;</span><span class="nx">C</span><span class="p">-</span><span class="nx">c</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nx">esc</span><span class="p">&gt;</span>:<span class="nx">silent</span>.<span class="nx">w</span> <span class="p">!</span><span class="nx">wscat</span><span class="p">&lt;</span><span class="nx">enter</span><span class="p">&gt;</span><span class="nx">i</span>
</span></span></code></pre></div><p>now whenever you use the key combo <kbd>ctl</kbd>+<kbd>c</kbd> it will send the current line in vim into maiden!</p>
</details>
<br>
<p>open up <code>dust/data/internorns/getting-started.lua</code> in your editor to learn how to use internorns.</p>
<h3 id="install">Install</h3>
<p>make sure you install all of the following (or update if you already have them):</p>
<pre tabindex="0"><code>;install https://github.com/schollz/mx.samples
;install https://github.com/schollz/supertonic
;install https://github.com/schollz/internorns
</code></pre><p>then restart your norns.</p>
<p><a href="https://github.com/schollz/internorns" target="_blank" >https://github.com/schollz/internorns</a></p>
]]></description>
    </item>
    
    <item>
	    <title>AI drummer</title>
	    <image>https://schollz.com/img/supertonic.png</image>
      <link>/tinker/supertonic/</link>
      <pubDate>Mon, 31 May 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/supertonic/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/557258118" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>(<a href="https://www.instagram.com/p/CPjATuQBfp4/?utm_source=ig_web_copy_link" target="_blank" >info on what is happening in video</a>)</p>
<h2 id="introspection">Introspection</h2>
<p>This drum machine introspects by looking at any of the current drum patterns and generating a new drum pattern based specifically on that pattern and which instruments they are (e.G. A snare rhythm based on the kick pattern).</p>
<p>These generative rhythms are accomplished using <a href="https://github.Com/magenta/magenta/tree/master/magenta/models/music_vae" target="_blank" >google&rsquo;s &ldquo;Variational autoencoder&rdquo;</a> for drum performances. <a href="https://magenta.Tensorflow.Org/groovae" target="_blank" >their blog post</a> explains it best (and <a href="https://arxiv.Org/pdf/1803.05428.Pdf" target="_blank" >their paper</a> explains it better), but essentially they had professional drummers play an electronic drum-set for 12+ hours which was later used to feed a special kind of neural network. I used their model from this network and sampled it randomly to produce &ldquo;New&rdquo; groups of drum rhythms (&gt;~1,000,000 of them). Then I created probability distributions from calculating bayesian probabilities from each instrument to each other instrument, within each rhythm grouping. This probability table can then generate a snare drum pattern based on a kick drum pattern, or generate a hihat pattern based on a snare drum pattern, etc. Etc.</p>
<h2 id="sounds">Sounds</h2>
<p>The sounds for this drum machine come from a new engine which I call &ldquo;Supertonic&rdquo; because it is a as-close-as-I-can port of the <a href="https://soniccharge.Com/microtonic" target="_blank" >microtonic vst by soniccharge</a>.</p>
<p>The act of porting is not straightforward and the experience itself was a motivation for this script - it helped me to learn how to use supercollider as I tried to match up sounds between the vst and supercollider using my ear. I learned there is a lot of beautiful magic in microtonic that makes it sounds wonderful, and I doubt I got half of the magic that&rsquo;s in the actual vst (so this is by no means a replacement). Looking at the resulting engine you might notice some weird equations that are supposed to be approximating the magic behavior in the true microtonic. This script also includes a standalone supercollider drum machine to use with this engine (and conversion scripts to convert microtonic patches).</p>
<p>Here is a <a href="https://www.Instagram.Com/p/cpghujub2of/?Utm_source=ig_web_copy_link" target="_blank" >demo comparing microtonic and this engine</a>.</p>
<h2 id="drummer-in-a-box">Drummer in a box</h2>
<p>In the end, this script is a little drum machine in a box and also a new drum machine engine for norns, a little like @21echoes&rsquo;s <a href="https://norns.Community/authors/21echoes/cyrene" target="_blank" >cyrene</a>, @pangrus&rsquo;s <a href="https://norns.Community/authors/pangrus/hachi" target="_blank" >hachi</a>, or @justmat&rsquo;s <a href="https://norns.Community/authors/justmat/foulplay" target="_blank" >foulplay</a>.</p>
<p>For me personally, this script is an experiment. To try to answer the question: what is it like to perform with an ai generated rhythm section (I.E. Paralleling <a href="https://github.Com/schollz/pianoai" target="_blank" >what its like to play with a ai generated piano</a>)? Is it good? Surprisingly so, sometimes.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li>norns</li>
</ul>
<h2 id="documentation">Documentation</h2>
<p>all the parameters for the engine are in the <code>PARAM</code> menu, as well as preset loading.</p>
<p>on the main screen:</p>
<ul>
<li>K2 starts/stops</li>
<li>K3 toggles hit</li>
<li>E2 changes track (current is bright)</li>
<li>E3 changes position in track</li>
<li>hold K1 and turn E3 to erase</li>
</ul>
<p>this script automatically detects all midi keyboards and will start/stop based on midi start/stop events.</p>
<p>you can hold K3 and move E2 to lay down a lot of beats.</p>
<h3 id="introspection-1">introspection</h3>
<p>introspection requires downloading a prior table (~100 mb, not included in repo) and <code>sqlite3</code>. both of these can be installed by running this command in maiden:</p>
<pre tabindex="0"><code>os.execute(&#34;sudo apt install -y sqlite3; mkdir -p /home/we/dust/data/supertonic/; curl -L --progress-bar https://github.com/schollz/supertonic/releases/download/v1_ai/drum_ai_patterns.db &gt; /home/we/dust/data/supertonic/drum_ai_patterns.db&#34;)
</code></pre><p>once installed, you have some new button combos available:</p>
<ul>
<li>hold K1, then press K3 to generate a new pattern based on the highlighted pattern</li>
<li>hold K1 and turn E2 to change the highlighted pattern (basis of the generation)</li>
<li>hold K1 and press K2 to generate beats 17-32 based on beats 1-16 for current instrument</li>
</ul>
<h3 id="using-your-own-microtonic-presets">using your own microtonic presets</h3>
<p>if you have microtonic you can easily use your own microtonic preset. simply copy your microtonic preset file (something like <code>&lt;name&gt;.mtpreset</code>) and and save it into the <code>/home/we/dust/data/supertonic/presets</code> directory. then, you can then load these presets via the <code>PARAM &gt; SUPERTONIC &gt; preset</code> menu.</p>
<h3 id="converting-microtonic-presets-for-use-with-supercollider">converting microtonic presets for use with SuperCollider</h3>
<p>you can also use the engine directly with SuperCollider. the engine file is synced with a SuperCollider script, <code>lib/supertonic.scd</code>. an example drumset is in <code>lib/supertonic_drumset.scd</code>. you can easily get a SuperCollider file with your microtonic presets by running this lua script:</p>
<pre tabindex="0"><code>lua ~/dust/code/supertonic/lib/mtpreset2sc.lua /location/to/your/&lt;name&gt;.mtpreset ~/dust/data/supertonic/default.preset &gt; presets.sc
</code></pre><p><strong>known bugs</strong></p>
<p>the supertonic engine is pretty cpu-intensive, so if you have 4-5 instruments all doing fast patterns (or fast tempo) you will hit cpu walls and hear crunching. any ideas to improve cpu usage are welcome :)</p>
<p>the pattern generation (k1+k3 or k1+k2) runs asynchronously but I&rsquo;ve noticed that sometimes it might cause a little latency when using it while performing (generating patterns while playing).</p>
<p>if you aren&rsquo;t seeing any new randomly generate patterns when pressing K1+K3/K2, it could be that the pattern that you&rsquo;re using as a basis doesn&rsquo;t exist in the database (and therefore won&rsquo;t produce any new patterns).</p>
<p><strong>thanks</strong></p>
<p>the ex-dash patterning functions are from @license from the collaborative <a href="https://github.com/northern-information/song/" target="_blank" >song</a> project. the flying confetti is from @eigen&rsquo;s brilliant <a href="https://llllllll.co/t/p8-pico-8-wrapper-lib/37947" target="_blank" >pico-8 wrapper</a>. also thanks @dan_derks, our little discussion helped me figure out the beginnings of this thing. finally, big big thanks to @midouest who shared their microtonic supercollider project which showed me some tricks I had missed and also showed me I was on the right track (because our implementations had a lot of parallels).</p>
<h2 id="download">Download</h2>
<p>install via maiden or</p>
<pre tabindex="0"><code>;install https://github.com/schollz/supertonic
</code></pre><p>make sure to restart after installing because it includes a new engine.</p>
<p>note, that to use the introspection you must also install the probability database (<a href="https://llllllll.co/t/supertonic/45551#introspection-7" target="_blank" >instructions here</a>).</p>
<p><a href="https://github.com/schollz/supertonic" target="_blank" >https://github.com/schollz/supertonic</a></p>
]]></description>
    </item>
    
    <item>
	    <title>Chaotic modulation</title>
	    <image>https://schollz.com/img/wobblewobble.gif</image>
      <link>/tinker/wobblewobble/</link>
      <pubDate>Thu, 20 May 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/wobblewobble/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/553165797" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>with wobblewobble you can&hellip;</p>
<ul>
<li>assign each of the four crow outputs one of many different types of slowly oscillating modulation sources.</li>
<li>configure crow as midi-&gt;CV or midi-&gt;envelope atop any oscillation (or none)</li>
<li>visualize slow oscillations from crow inputs (&lt;7hz)</li>
</ul>
<p>this script was also designed to be a library (all functionality is available in the menu) so that it can be imported for crow functionality from other scripts. no examples of this yet, though.</p>
<h3 id="requirements">Requirements</h3>
<ul>
<li>norns</li>
<li>crow</li>
</ul>
<h3 id="documentation">Documentation</h3>
<p>from the main norns screen you can:</p>
<ul>
<li>hold K1 and K2/K3 to switch crow</li>
<li>press K2/K3 to switch modulator</li>
<li>E1 changes frequency</li>
<li>E2/E3changes lfo min/max</li>
<li>K1+E2 or K1+E3 changes meta</li>
</ul>
<p>the params menu has more options, including:</p>
<ul>
<li>clamping the absolute minimum and maximum values</li>
<li>add midi input to control pitch (top note, any note) or add an envelope</li>
<li>meta params for the modulators</li>
<li>change slew rate</li>
</ul>
<p>oscillators are configured in SuperCollider making it easy to configure / design / add new oscillators, including complex ones from the <a href="https://doc.sccode.org/Browse.html#UGens%3EGenerators%3EChaotic" target="_blank" >chaotic UGens</a>. current oscillators:</p>
<h4 id="constant">constant</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861929-eb287d00-b891-11eb-9efd-2a09f0142d5f.PNG" alt="constant"></p>
<h4 id="sine">sine</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861927-ea8fe680-b891-11eb-9fe1-7ce6c2f93c81.PNG" alt="sine"></p>
<h4 id="triangle">triangle</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861926-ea8fe680-b891-11eb-85b1-8a9cc5df7c94.PNG" alt="triangle"></p>
<h4 id="wobbly-sine">wobbly sine</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861924-e9f75000-b891-11eb-9b49-b1df6d0cc18e.PNG" alt="wobblysine"></p>
<h4 id="snek">snek</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861922-e95eb980-b891-11eb-9d31-bc03bc69210f.PNG" alt="snek"></p>
<h4 id="lorenzian">lorenzian</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861920-e95eb980-b891-11eb-9705-3b80d592c4fe.PNG" alt="lorenz"></p>
<h4 id="henonian">henonian</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861918-e8c62300-b891-11eb-97ed-3affa265dd18.PNG" alt="henon"></p>
<h4 id="random-walk">random walk</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861917-e82d8c80-b891-11eb-940b-4877262f5110.PNG" alt="randomwalk"></p>
<h4 id="latoocarfian">latoocarfian</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861930-eb287d00-b891-11eb-9225-3f78d48dd050.PNG" alt="LatoocarfianC"></p>
<h4 id="standard-chaotic">standard chaotic</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861935-ec59aa00-b891-11eb-838e-cbaed31b23e8.PNG" alt="standard"></p>
<h4 id="fbsine">fbsine</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861938-ecf24080-b891-11eb-9475-281177b24c5d.PNG" alt="fbsine"></p>
<h4 id="quad">quad</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861937-ec59aa00-b891-11eb-965f-2856069def06.PNG" alt="quad"></p>
<h4 id="gingerbreadman">gingerbreadman</h4>
<p><img src="https://user-images.githubusercontent.com/6550035/118861933-ebc11380-b891-11eb-96b3-118671315bd9.PNG" alt="gingerbread"></p>
<p>many many thanks to @proswell for the excellent additions (grid support, five more modulators, adding inputs, adding metas) as well as testing.</p>
<p>any other PRs are welcomed with open wings.</p>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/wobblewobble
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>A PO-33 clone</title>
	    <image>https://schollz.com/img/thirtythree.jpg</image>
      <link>/tinker/thirtythree/</link>
      <pubDate>Wed, 05 May 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/thirtythree/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/545281946" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>I love the <a href="https://teenage.engineering/store/po-33/" target="_blank" >po-33</a> and for a long time I&rsquo;ve wanted something similar for norns (e.g. a micro-sampler/splicer + sequencer). I tried making something similar in <a href="https://llllllll.co/t/abacus" target="_blank" >abacus</a> but I didn&rsquo;t have a grid so the ux was very limited and ultimately gave me a bad flow. after making <a href="https://llllllll.co/t/amen" target="_blank" >amen</a> which is a great looper+fx for me, I realized that a lot of my work could be re-used to make a splicer+sequencer too. instead of designing a ux from scratch like I did for abacus, I decided to just copy the ux from the po-33 to have complete skill transferability between instruments (much like you would have between two pianos or two saxophones). however there are some key differences:</p>
<ul>
<li>thirtythree is based in the norns which has higher sound quality (48khz, 24bit, stereo samples (though higher is not always better))</li>
<li>thirtythree fx are <em>sound</em>-specific instead of global (see <code>PARAMS</code> menu to toggle global fx in the po-33).</li>
<li>thirtythree fx <em>stack</em> (except looping fx) so multiple fx can be applied simultanouesly.</li>
<li>in addition to recording from line-in, you can also load a file into any bank (see <code>PARAMS</code> menu to toggle this bheavior).</li>
<li>you have two choices of layouts (see the <code>PARAMS</code> menu) - the classic 5x5 type layout and a compressed 4x6 layout that lets you stamp more of the thirtythree apps across the grid.</li>
<li>you can chain up to four operators on a grid, and there is ~10 note polyphony shared across all of the operators.</li>
<li>thirtythree doesn&rsquo;t save instantaneously like the po-33 does. you can save manually using the key combo (below) or wait for the auto-save to occur (which occurs after idling for ~3 seconds).</li>
<li>thirtythree dumps can be shared via the <a href="https://norns.online/share/thirtythree/" target="_blank" >norns.online cloud</a>.</li>
</ul>
<h2 id="requirements">requirements</h2>
<ul>
<li>norns</li>
<li>monome grid or most midi grids</li>
</ul>
<h2 id="documentation">documentation</h2>
<p><a href="https://teenage.engineering/guides/po-33/en" target="_blank" >the official te guide</a> for the po-33 explains the usage for this app as thirtythree follows all of the same key combos. basics:</p>
<ul>
<li>melodic (buttons 1-8) and drum splicing (buttons 9-16)</li>
<li>record with <kbd>record</kbd>+<kbd>1-16</kbd> (option to load files instead in parameters)</li>
<li>select sound with <kbd>sound</kbd>+<kbd>1-16</kbd></li>
<li>write mode activated with <kb>write</kbd></li>
<li>select pattern with <kbd>pattern</kbd>+<kbd>1-16</kbd></li>
<li>play pattern with <kbd>play</kbd></li>
<li>toggle parameters with <kbd>fx</kbd></li>
<li>adjust tone (pitch+volume) with <kbd>E2</kbd> and <kbd>E3</kbd></li>
<li>adjust filter (lp+hp) with <kbd>E2</kbd> and <kbd>E3</kbd></li>
<li>adjust trim (start+end) with <kbd>E2</kbd> and <kbd>E3</kbd></li>
<li>delete sound with <kbd>record</kbd>+<kbd>sound</kbd></li>
<li>(not implemented) copy slice with <kbd>write</kbd>+<kbd>sound</kbd>+<kbd>9-16</kbd>+<kbd>1-16</kbd></li>
<li>add effects with <kbd>fx</kbd>+<kbd>1-16</kbd></li>
<li>change swing with <kbd>bpm</kbd>+<kbd>K3</kbd></li>
<li>change tempo tapping <kbd>bpm</kbd></li>
<li>change tempo with <kbd>bpm</kbd>+<kbd>K3</kbd></li>
<li>change master volume with <kbd>bpm</kbd>+<kbd>1-16</kbd></li>
<li>parameter locking with <kbd>write</kbd>+(<kbd>K2</kbd> or <kbd>K3</kbd>)</li>
<li>chain pattern with <kbd>pattern</kbd>+<kbd>1-16</kbd></li>
<li>copy pattern with <kbd>write</kbd>+<kbd>pattern</kbd>+<kbd>1-16</kbd></li>
<li>clear entire pattern with <kbd>record</kbd>+<kbd>pattern</kbd></li>
<li>(not implemented) clear current sound pattern with <kbd>record</kbd>+<kbd>sound</kbd>+<kbd>pattern</kbd> (<em>new</em>)</li>
<li>backup data with <kbd>write</kbd>+<kbd>sound</kbd>+<kbd>play</kbd></li>
<li>restore data with <kbd>write</kbd>+<kbd>sound</kbd>+<kbd>record</kbd></li>
</ul>
<p>when in the &ldquo;trim&rdquo; mode, you can use E2 or E3 to jog the endpoints. use E1 to zoom in to the last endpoint moved. in the tone or filter mode, you can adjust things with just E2 or E3.</p>
<p>here are the two possible layouts available to stamp the grid with:</p>
<p><img src="/img/layout.jpg" alt="layouts"></p>
<h3 id="thirtythree-effects">thirtythree effects</h3>
<p>the fx are the same, except unison effects replaced by stereo auto-panning and a bitcrush. some fx stack (looping / scratching do not).</p>
<ol>
<li>loop 16</li>
<li>loop 12</li>
<li>loop short</li>
<li>loop shorter</li>
<li><del>unison</del> autopan</li>
<li><del>unison low</del> bitcrush</li>
<li>octave up</li>
<li>octave down</li>
<li>stutter 4</li>
<li>stutter 3</li>
<li>scratch</li>
<li>scratch fast</li>
<li>6/8 quantize</li>
<li>retrigger pattern</li>
<li>reverse</li>
<li>none</li>
</ol>
<h3 id="saving--loading">saving / loading</h3>
<p>thirtythree automatically saves your current state when you are idle - this is the &ldquo;default&rdquo; that will be loaded each time you open thirtythree (just like when you turn on a po-33). you can also use the key combo to save (see above).</p>
<p>in addition to automatic saving, you can save your current state to a separte file using <code>PSET &gt; SAVE</code> and load with <code>PSET &gt; LOAD</code>.</p>
<p>thirtythree also optionally lets you share your work with others. first install and run <code>norns.online</code>:</p>
<pre tabindex="0"><code>;install https://github.com/schollz/norns.online
</code></pre><p>once you run that app and choose a username, you can then use thirtythree to upload+download shares. there will be a new menu <code>PARAMS &gt; SHARE &gt; upload/download</code> which you can use to share or backup your work to the cloud.</p>
<h3 id="known-limitationsbugs">known limitations/bugs</h3>
<ul>
<li>backups will not restore if you move/delete the <code>~/dust/audio/thirtythree</code> audio folder, where all the audio data is stored.</li>
<li>looping fx don&rsquo;t stack.</li>
<li>sometimes there is race condition in the bpm, if you get wacko pattern stepping, restart. so far I haven&rsquo;t been able to reproduce consistently.</li>
<li>using 6/8 beat fx might cause operators to get out of sync. if this happens, stop all the patterns and start them again and they should sync back up.</li>
</ul>
<h2 id="thanks">thanks</h2>
<p>thank you @license and @catfact for never ceasing to teach me a half dozen supercollider tricks in half as many lines of code. thanks to @proswell and <a href="https://www.instagram.com/crazyemperor893/" target="_blank" >@CrazyEmporer893</a> for beta testing. thanks to @eigen for the <a href="https://github.com/p3r7/p8" target="_blank" >p8 library</a> which was a jumping off point for the graphics and source of the <a href="https://github.com/p3r7/p8/blob/main/manga_effect.lua" target="_blank" >the manga</a>. thanks to @glia for allow me to include <a href="https://ifizu.bandcamp.com/album/yelidek" target="_blank" >a yelidek kit</a> as the default drum kit.  thank you <a href="https://www.instagram.com/jredou_ko/" target="_blank" >@jredou_ko</a> for help with the graphics.</p>
<h2 id="install">install</h2>
<p>install with</p>
<pre tabindex="0"><code>;install https://github.com/schollz/thirtythree
</code></pre><p>the first time you run thirtythree it will update your norns with the <a href="https://aubio.org/" target="_blank" >aubio library</a> which is used to generate default onsets. this might take 10-15 seconds.</p>
]]></description>
    </item>
    
    <item>
	    <title>SuperCollider sample playback</title>
	    <image>https://schollz.com/img/sc2/header.png</image>
      <link>/tinker/sampler/</link>
      <pubDate>Wed, 05 May 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/sampler/</guid>
      <description><![CDATA[<!--ffmpeg -y -i "C:\Users\zacks\Documents\SuperCollider\Recordings\SC_210506_090419.aiff" -af silenceremove=1:0:-50dB 31.mp3-->
<p><a href="https://supercollider.github.io/" target="_blank" >SuperCollider</a> is a free platform for audio synthesis and algorithm composition. It really excels at real-time analysis, synthesis and processing and provides a framework for seamless combination of audio techniques like additive and subtractive synthesis (as explored <a href="/blog/tone-to-drone" >in my previous tutorial on drones</a>) or be used for sophisticated sampling+splicing, the focus of this tutorial.</p>
<p>A sampler+splicer is useful when you have an audiofile and you want to cut it up into pieces and play it back in specific order. You can do this and change the tempo of a song by playing back snippets faster, or <a href="https://www.youtube.com/watch?v=Cfj6NTobTA4" target="_blank" >shuffle drum breaks</a> to make new beats, or <a href="https://op1.fun/users/josephmcarr/patches/patch-drum-58" target="_blank" >map specific sounds</a> from a single audio file.</p>
<h2 id="before-you-begin">Before you begin</h2>
<p>Before starting, make sure you have SuperCollider and plugins (if you want).</p>
<ul>
<li><a href="https://supercollider.github.io/download" target="_blank" >Install SuperCollider</a></li>
<li><a href="https://supercollider.github.io/sc3-plugins/" target="_blank" >Install SuperCollider plugins</a> (optional, but recommended)</li>
</ul>
<p>If you have these basic things installed, you are good to go. Feel free to do a SuperCollider tutorial if you want, but I&rsquo;ll assume you don&rsquo;t know how to use it.</p>
<p>With SuperCollider installed, you will need to run the program and then start the server using <kbd>Ctl</kbd>+<kbd>B</kbd>.</p>
<p>Here is a link to download all the files for this tutorial: <a href="https://github.com/schollz/sc-tutorial/archive/refs/heads/main.zip" target="_blank" >https://github.com/schollz/sc-tutorial/archive/refs/heads/main.zip</a></p>
<h3 id="1-load-a-buffer-with-audio">1. Load a buffer with audio</h3>
<p>We are going to start playing with a simple piano loop that I got from the radio:</p>
<p><audio src="/img/sc2/pianochords.wav" class="waveform"></audio></p>
<p>Let&rsquo;s first load this into SuperCollider, by running the following lines:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
  b=Buffer.read(s,thisProcess.nowExecutingPath.dirname++&#34;sounds/pianochords.wav&#34;);
)
</code></pre><h3 id="2-use-playbuf-to-play-sample">2. Use <code>PlayBuf</code> to play sample</h3>
<p>And now lets play it back using the simple <a href="https://doc.sccode.org/Classes/PlayBuf.html" target="_blank" ><code>PlayBuf</code></a> &ldquo;UGen&rdquo;:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
SynthDef(&#34;PlayBufPlayer&#34;, {
    arg out = 0, bufnum = 0;
    var snd;
    snd=PlayBuf.ar(2, bufnum, BufRateScale.kr(bufnum), doneAction: Done.freeSelf);
    Out.ar(out,snd)
}).play(s, [\out, 0, \bufnum, b]);
)
</code></pre><p>When running that code you should immediately hear it play back!</p>
<p>While this already works as a sampler (it plays back a sample), it actually won&rsquo;t let us set the endpoints so we can <em>splice</em> the sample. Let&rsquo;s do that next.</p>
<h3 id="3-use-bufrd--phasor-to-precisely-play-sample">3. Use <code>BufRd</code> + <code>Phasor</code> to precisely play sample</h3>
<p>In order to precisely control the position of playback we must use something called a <code>Phasor</code>. A <code>Phasor</code> is basically a sawtooth-like signal that increments at a specific rate. It is really useful as a position index, to set the position of playback, because you can control its start and end poitns and the rate.</p>
<p>Here is what a <code>Phasor</code> will look like:</p>
<p><img src="/img/sc2/phasor1.png" alt="{ Phasor.kr(0,0.5,0,100) }.plot(1)"></p>
<p>And instead of going from 0-100 we can change it to whatever we want while also changing the rate:</p>
<p><img src="/img/sc2/phasor2.png" alt="{ Phasor.kr(0,0.5,0,100) }.plot(1)"></p>
<p>We will introduce a lot of code to do this, but I&rsquo;ll go through it piece by piece:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x=SynthDef(&#34;PlayBufPlayer&#34;, {
    arg out=0, bufnum=0, rate=1, start=0, end=1;
    var snd,pos,frames;

    rate = rate*BufRateScale.kr(bufnum);
    frames = BufFrames.kr(bufnum);

    pos=Phasor.ar(
        rate:rate,
        start:start*frames,
        end:end*frames,
        resetPos:start*frames,
    );

    snd=BufRd.ar(
        numChannels:2,
        bufnum:bufnum,
        phase:pos,
        loop:0,
        interpolation:4,
    );
    Out.ar(out,snd)
}).play(s, [\out, 0, \bufnum, b]);
)
</code></pre><p>The <code>rate</code> is now an argument and default is 1. However, it gets modulated by <code>BufRateScale.kr(bufnum)</code> where <code>bufnum</code> is a reference to the audio file. This modulation is a scale based on the sample rate of the audio file and the sample rate of the server. In the case the two sample rates don&rsquo;t match, then you do need to change the speed you playback a sample to make sure it is exactly the same pitch when played at the other sample rate at a rate of 1.</p>
<p>The <code>Phasor</code> has <code>start</code> and <code>end</code> points and a <code>rate</code> and a <code>resetPos</code> which is the position it resets. The <code>Phasor</code> takes inputs in numbers of <em>frames</em> so we multiple the <code>start</code> and <code>end</code> points (which I am denoting to always be between 0 and 1) by the number of frames.</p>
<p>The <code>BufRd</code> is where the playback happens. We set the position of playback using the <code>phase</code> parameter. Notice we set <code>loop</code> to <code>0</code>, but in actuality, because we are using a <code>Phasor</code> this will be ignored.</p>
<p>We can now change the start and end positions by running this command after running the <code>SynthDef</code> above:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">x.set(\start,0.5,\end,0.6);
</code></pre><p><audio src="/img/sc2/31.mp3" class="waveform"></audio></p>
<p>One thing you might notice is that you cannot get this sample to reset if you run that command more than once. Resetting is a really useful thing, and we can fix this <code>SynthDef</code> to allow that.</p>
<h3 id="4-use-trigger-to-allow-resetting-sample">4. Use <em>trigger</em> to allow resetting sample</h3>
<p>This fix is only one line, and adding a new argument, <code>t_trig</code>. This is a special argument and when it goes from <code>0</code> to <code>1</code> it can cause a change in a <code>UGen</code>. In this case we will use it to cause a change in <code>Phasor</code>, which will make it reset.</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x=SynthDef(&#34;PlayBufPlayer&#34;, {
    arg out=0, bufnum=0, rate=1, start=0, end=1, t_trig=0; // NEW t_trig!
    var snd,pos,frames;

    rate = rate*BufRateScale.kr(bufnum);
    frames = BufFrames.kr(bufnum);

    pos=Phasor.ar(
        trig:t_trig, // NEW!
        rate:rate,
        start:start*frames,
        end:end*frames,
        resetPos:start*frames,
    );

    snd=BufRd.ar(
        numChannels:2,
        bufnum:bufnum,
        phase:pos,
        loop:0,
        interpolation:4,
    );
    Out.ar(out,snd)
}).play(s, [\out, 0, \bufnum, b]);
)
</code></pre><p>Now you can send various commands to this <code>SynthDef</code> which can cause it to reset to the new start position:</p>
<pre tabindex="0"><code>x.set(\t_trig,1,\start,0,\end,1)
x.set(\t_trig,1,\start,0.5,\end,1)
</code></pre><p><audio src="/img/sc2/41.mp3" class="waveform"></audio></p>
<p>You can hear in that audio that I reset the position multiple times.</p>
<p>One problem that persists though is that, as mentioned earlier, we are using a <code>Phasor</code> which loops forever and overrides the <code>loop</code> parameter of the <code>BufRd</code> UGen. It would be better to have an option to set the loops to any number you want.</p>
<h3 id="5-use-an-envelope-env-to-control-looping">5. Use an envelope, <code>Env</code>, to control looping</h3>
<p>It turns out we can do a little trick to get a precise loop - we can use an <em>envelope</em>. An envelope is essentially a curve in time that we can multiple by the output amplitude. The function of the curve is to shape the amplitude, and in this case we will shape it so it turns on and then turns off after a certain amount of time.</p>
<p>A basic envelope is generated like this:</p>
<pre tabindex="0"><code>({ 
EnvGen.ar(
    Env.new(
        levels: [0,1,1,0],
        times: [0,1,0],
    ),
)
}.plot(2);)
</code></pre><p>which generates an envelope that lasts 1 second and then goes to 0. It looks like this:</p>
<p><img src="/img/sc2/env1.png" alt="Simple square envelope"></p>
<p>There are all sorts of envelopes you can make. We can make this one fancier by having it fade in/out a little smoother by adding some time to the &ldquo;attack&rdquo; and &ldquo;release&rdquo; part of the envelope:</p>
<pre tabindex="0"><code>(
{ EnvGen.ar(
    Env.new(
        levels: [0,1,1,0],
        times: [0.2,1-0.4,0.2],
        curve:\sine,
    ),
)
}.plot(2);
)
</code></pre><p><img src="/img/sc2/env2.png" alt="Simple square envelope"></p>
<p>Great! Well all we need to do to preserve a loop is to calculate how long the duration of the envelope should be. This involves a little math, but it should be related to the number of frames, the rate (1/2 the rate = twice the time), the sample rate, and the number of loops:</p>
<pre tabindex="0"><code>duration = frames*(end-start)/rate.abs/s.sampleRate*loops
</code></pre><p>We then just need to trigger the envelope when we trigger our sample and voila!</p>
<pre tabindex="0"><code>(
x=SynthDef(&#34;PlayBufPlayer&#34;, {
    arg out=0, bufnum=0, rate=1, start=0, end=1, t_trig=1,
    loops=1; // NEW
    var snd,pos,frames,duration,env;

    rate = rate*BufRateScale.kr(bufnum);
    frames = BufFrames.kr(bufnum);
    duration = frames*(end-start)/rate/s.sampleRate*loops; // NEW

    // envelope to clamp looping
    env=EnvGen.ar(
        Env.new(
            levels: [0,1,1,0],
            times: [0,duration-0.01,0.01],
            curve:\sine,
        ),
        gate:t_trig,
    );

    pos=Phasor.ar(
        trig:t_trig,
        rate:rate,
        start:start*frames,
        end:end*frames,
        resetPos:start*frames,
    );

    snd=BufRd.ar(
        numChannels:2,
        bufnum:bufnum,
        phase:pos,
        interpolation:4,
    );

    snd = snd * env;

    Out.ar(out,snd)
}).play(s, [\out, 0, \bufnum, b]);
)
</code></pre><p>Now we can set the loop number, <em>and</em> reset the sample, <em>and</em> control the start and stop positions:</p>
<pre tabindex="0"><code>x.set(\t_trig,1,\start,0.0,\end,0.15,\loops,1)
x.set(\t_trig,1,\start,0.5,\end,0.52,\loops,6)
</code></pre><p>This sounds like the following:</p>
<p><audio src="/img/sc2/51.mp3" class="waveform"></audio></p>
<p>You can see that loops stop and stay stopped! And you can make it loop as many times as you want.</p>
<p>But what happens if we reverse? If you change the rate to <code>-1</code> it actually won&rsquo;t play:</p>
<pre tabindex="0"><code>x.set(\t_trig,1,\start,0.5,\end,0.6,\loops,3,\rate,-1)
</code></pre><p>Its because the <code>start</code> and <code>end</code> points need to swap. If we do that, then we can succesfully play in reverse:</p>
<pre tabindex="0"><code>x.set(\t_trig,1,\start,0.6,\end,0.5,\loops,3,\rate,-1)
</code></pre><p>But, lets alter the code above so that it works in reverse without having the <code>start</code> and <code>end</code> points switching around!</p>
<h3 id="6-automatically-swap-startend-points-when-reversing">6. Automatically swap start/end points when reversing</h3>
<p>To swap the start/end points we need to check the rate and add some logic about whether the rate is positive or negative. You can&rsquo;t really use <code>if</code>-statements in SuperCollider (well you can but its weird), but we can do this easily with math. For example, the <code>start</code> parameter of the <code>Phasor</code> can calculate a binary whether the <code>rate</code> is positive or negative and then make a sum based on it:</p>
<pre tabindex="0"><code>start:(((rate&gt;0)*start)+((rate&lt;0)*end))*frames
</code></pre><p>It looks complicated but its quite simple. If the <code>rate&gt;0</code> then the second part in the parentheses is <code>0</code> and its just <code>start</code>. If the <code>rate&lt;0</code> then the first part is <code>0</code> and then it just switches to <code>end</code>.</p>
<p>The final <code>SynthDef</code> looks like this:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x=SynthDef(&#34;PlayBufPlayer&#34;, {
    arg out=0, bufnum=0, rate=1, start=0, end=1, t_trig=1,
    loops=1;
    var snd,pos,frames,duration,env;

    rate = rate*BufRateScale.kr(bufnum);
    frames = BufFrames.kr(bufnum);
    duration = frames*(end-start)/rate.abs/s.sampleRate*loops; // use rate.abs instead now

    // envelope to clamp looping
    env=EnvGen.ar(
        Env.new(
            levels: [0,1,1,0],
            times: [0,duration,0],
        ),
        gate:t_trig,
    );

    pos=Phasor.ar(
        trig:t_trig,
        rate:rate,
        start:(((rate&gt;0)*start)+((rate&lt;0)*end))*frames,
        end:(((rate&gt;0)*end)+((rate&lt;0)*start))*frames,
        resetPos:(((rate&gt;0)*start)+((rate&lt;0)*end))*frames,
    );

    snd=BufRd.ar(
        numChannels:2,
        bufnum:bufnum,
        phase:pos,
        interpolation:4,
    );

    snd = snd * env;

    Out.ar(out,snd)
}).play(s, [\out, 0, \bufnum, b]);
)
</code></pre><p>And when we use the problematic statement above, we can see that it just works:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">x.set(\t_trig,1,\start,0.5,\end,0.6,\loops,3,\rate,-1)
</code></pre><p><audio src="/img/sc2/61.mp3" class="waveform"></audio></p>
<p>Okay, now one more thing. You may have noticed in playing around that there is an annoying &ldquo;click&rdquo; or &ldquo;pop&rdquo; when you reset the sample. For example, try to send the following a bunch of times really fast you&rsquo;ll hear an audible noise artifact from the switching:</p>
<pre tabindex="0"><code>x.set(\t_trig,1,\start,0.2,\end,0.6,\loops,1,\rate,1)
</code></pre><p><audio src="/img/sc2/62.mp3" class="waveform"></audio></p>
<p>It&rsquo;s unpleasant right? Well we can make one more improvement to remove this type of thing.</p>
<h3 id="7-preventing-switching-pops">7. Preventing switching pops</h3>
<p>The main problem of pops caused by switching is that there isn&rsquo;t a smooth transition to the next position. The smooth transition we expect actually needs to come from the audio itself and not the position. To make a smooth transition then, we need to <em>crossfade</em> between the old loop and the new loop.</p>
<p>Doing this is not nearly as hard as it seems at first. We already have one loop playing. We need to make another loop. Then we just need to make a new trigger, based on <code>t_trig</code> which we already use, that flips between triggering the two loops. We can use a special UGen for this called <code>ToggleFF</code> and then another UGen called <code>Latch</code> which will allow us to change only one of the loops at a time, alternating between the two.</p>
<p>Below is the new code. We still have the same arguments, but we have new variables for the two loops (<code>startA+B</code>, <code>endA+B</code>, <code>crossfade</code>, and <code>aORB</code> which is the new trigger). The <code>crossfade</code> is doing the major work here, we can create the audio crossfade by using something called <code>Lag</code> which will transition between the two values slowly.</p>
<p>For example, this is a 50 ms crossfade:</p>
<pre tabindex="0"><code>crossfade=Lag.ar(K2A.ar(aOrB),0.05);
</code></pre><p>This is what the new code ends up being:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x=SynthDef(&#34;PlayBufPlayer&#34;, {
    arg out=0, bufnum=0, rate=1, start=0, end=1, t_trig=0,
    loops=1;
    var snd,snd2,pos,pos2,frames,duration,env;
    var startA,endA,startB,endB,crossfade,aOrB;

    // latch to change trigger between the two
    aOrB=ToggleFF.kr(t_trig);
    startA=Latch.kr(start,aOrB);
    endA=Latch.kr(end,aOrB);
    startB=Latch.kr(start,1-aOrB);
    endB=Latch.kr(end,1-aOrB);
    crossfade=Lag.ar(K2A.ar(aOrB),0.05);


    rate = rate*BufRateScale.kr(bufnum);
    frames = BufFrames.kr(bufnum);
    duration = frames*(end-start)/rate.abs/s.sampleRate*loops;

    // envelope to clamp looping
    env=EnvGen.ar(
        Env.new(
            levels: [0,1,1,0],
            times: [0,duration-0.05,0.05],
        ),
        gate:t_trig,
    );

    pos=Phasor.ar(
        trig:aOrB,
        rate:rate,
        start:(((rate&gt;0)*startA)+((rate&lt;0)*endA))*frames,
        end:(((rate&gt;0)*endA)+((rate&lt;0)*startA))*frames,
        resetPos:(((rate&gt;0)*startA)+((rate&lt;0)*endA))*frames,
    );
    snd=BufRd.ar(
        numChannels:2,
        bufnum:bufnum,
        phase:pos,
        interpolation:4,
    );

    // add a second reader
    pos2=Phasor.ar(
        trig:(1-aOrB),
        rate:rate,
        start:(((rate&gt;0)*startB)+((rate&lt;0)*endB))*frames,
        end:(((rate&gt;0)*endB)+((rate&lt;0)*startB))*frames,
        resetPos:(((rate&gt;0)*startB)+((rate&lt;0)*endB))*frames,
    );
    snd2=BufRd.ar(
        numChannels:2,
        bufnum:bufnum,
        phase:pos2,
        interpolation:4,
    );

    Out.ar(out,(crossfade*snd)+((1-crossfade)*snd2) * env)
}).play(s, [\out, 0, \bufnum, b]);
)
</code></pre><p>And we can try this out on the problematic loop from before:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">x.set(\t_trig,1,\start,0.2,\end,0.6,\loops,1,\rate,1)
</code></pre><p>Even though it is triggered a bunch of times, it sounds smooth! This is because, underneath it all, its switching back and forth between loops while crossfading them simultaneously.</p>
<p><audio src="/img/sc2/71.mp3" class="waveform"></audio></p>
<h3 id="8-cool-but-now-what">8. COOL, but now what?</h3>
<p>Now you can branch off with this <code>SynthDef</code> and do all sorts of things. You can add effects to it (maybe I can add more about this). One thing I like to do is that you can immediately do &ldquo;onset detection&rdquo;, gather splices, and pattern them!</p>
<p>Here&rsquo;s a synthdef I cobbled together to determine splicing from arbitrary samples:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
SynthDef.removeAt(&#34;OnsetDetection&#34;);

o = OSCFunc({ arg msg, time;
	[time, msg].postln;
	if (msg[3]-~pos1.last&gt;(0.15*s.sampleRate/b.numFrames),{
		&#34;added&#34;.postln;
		~pos1.add(msg[3]);
	},{});
},&#39;/tr&#39;, s.addr);

SynthDef(&#34;OnsetDetection&#34;, {
    arg bufnum, out, threshold;
    var sig, chain, onsets, pips, pos, env, speedup=1;

    env=EnvGen.ar(
        Env.new(
            levels: [0,1,1,0],
            times: [0,BufDur.kr(bufnum)/speedup,0],
            curve:\sine,
        ),
        doneAction: Done.freeSelf
    );

    pos=Phasor.ar(0, BufRateScale.kr(bufnum)*speedup, 0, BufFrames.kr(bufnum),0);
    sig = BufRd.ar(2, bufnum, pos,0);

    chain = FFT(LocalBuf(512), sig[0]+sig[1]);

    onsets = Onsets.kr(chain, threshold, \rcomplex,mingap:160);

    // You&#39;ll hear percussive &#34;ticks&#34; whenever an onset is detected
    pips = WhiteNoise.ar(EnvGen.kr(Env.perc(0.001, 0.1, 0.2), onsets));
    SendTrig.kr(onsets,0,Clip.kr((pos-300)/BufFrames.kr(bufnum),0,1));
    Out.ar(out,Pan2.ar(sig, -0.75, 0.2) + Pan2.ar(pips, 0.75, 1));
}).add;
)
</code></pre><p>Once defined, you can run it with the following:</p>
<pre tabindex="0"><code>(
~pos1=List.new();
~pos1.add(0);
b=Buffer.read(s,thisProcess.nowExecutingPath.dirname++&#34;/sounds/pianochords.wav&#34;,
    action:{
        Synth(&#34;OnsetDetection&#34;,[\out,0,\bufnum,b.bufnum,\threshold,2.0]); // CHANGE THRESHOLD TO SOMETHING THAT WORKS
});
)
</code></pre><p>Make sure you change the threshold (above its <code>2.0</code>) to something where you hear a &ldquo;click&rdquo; on the appropriate onset. For example, using the piano from above:</p>
<p><audio src="/img/sc2/81.mp3" class="waveform"></audio></p>
<p>You can hear it automatically detect onsets. And now you can playback individual splices:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">f = {arg i;x.set(\t_trig,1,\start,~pos1[i],\end,~pos1[i+1]);}
f.value(0); // plays splice &#34;0&#34;
f.value(1); // plays splice &#34;1&#34;
</code></pre><p><audio src="/img/sc2/82.mp3" class="waveform"></audio></p>
<p>Or visualize them:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">g = {arg i; b.loadToFloatArray(~pos1[i]*b.numFrames,(~pos1[i+1]-~pos1[i])*b.numFrames,{arg array; a=array; {~temp=a.plot;~temp.setProperties(\gridOnX,false,\gridOnY,false)}.defer; })};
g.value(0);
g.value(1); // visualize it
</code></pre><p><img src="/img/sc2/82.png" alt="Splice 2"></p>
<p>Or sequence them:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
~player=[1,0,2,0,2,0,0,0,3,3,3,3,4,0,3,0];
t = Task({
    inf.do({ arg i;
        var toPlay;
        0.125.wait;
        toPlay = ~player[i%~player.size].postln;
        if (toPlay&gt;0,{
            toPlay.postln;
            x.set(\t_trig,1,\start,~pos1[toPlay-1],\end,~pos1[toPlay],\loops,1);
        },{});
    });
}).play;
)
</code></pre><p><audio src="/img/sc2/83.mp3" class="waveform"></audio></p>
<p>Plus much more&hellip;but you&rsquo;ve had enough right?</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>Fixing a SVE in croc</title>
	    <image>https://schollz.com/img/2018.png</image>
      <link>/tinker/croc9/</link>
      <pubDate>Fri, 23 Apr 2021 15:45:39 -0700</pubDate>
      
      <guid>/tinker/croc9/</guid>
      <description><![CDATA[<p>On Thursday, 04/16/2021, I was made aware of vulnerabilities present in <a href="https://github.com/schollz/croc" target="_blank" >croc</a>, a file-transfer utility that I have been maintaining for several years (for more about croc, see my <a href="/blog/croc6" >previous post</a>).</p>
<p>The vulnerabilities were discovered and disclosed by Aaron Kaiser of the <a href="https://redrocket.club/imprint/" target="_blank" >RedRocket CTF team</a>. What Aaron showed me was that on croc (version 8) an exploit could be created to produce a rogue receiver that could backdoor and then bruteforce the SPAKE2 algorithm and receive a file. This attack, though serious, is not covert, as the sender would notice their file being transferred to someplace other than the true receiver. The technical details of this attack are very interesting, thoroughly documented, and cleverly executed and can be found in <a href="https://redrocket.club/posts/croc/" target="_blank" >RedRocket&rsquo;s blog post about croc&rsquo;s security flaws</a>.</p>
<p>Luckily, this critical vulnerability seems to have not been exploited. Users that utilized the public relay could have been exploited, but they would have been made aware immediately by recognizing that the sender transferred a file while the receiver never received a file. So far, I have no reports of any users experiencing this behavior. (If you have experienced this behavior, please let me know ASAP via <a href="https://keybase.io/schollz" target="_blank" >keybase</a>). Also, fortunately, the exploit was found and raised to me privately so I don&rsquo;t believe it was ever used in nefarious ways. To my knowledge, the exploit found would not work on private relays since it requires to have the rogue recipient first bind itself to the relay (which it cannot do if a relay is set to private).</p>
<p>RedRocket wrote a detailed analysis of croc and outlined several points of failure within it, including this most serious exploit mentioned above as well as a few others. This post will address these vulnerabilities and the changes I made to croc to solve them. These changes are reflected in croc, version 9.</p>
<style>
blockquote {
	font-style: italic;
	font-weight: 900;
	padding-top:  0.5em;
	padding-bottom: 0.5em;
}
</style>
<h2 id="fixing-vulnerabilities-and-releasing-croc-version-9">Fixing vulnerabilities and releasing croc version 9</h2>
<p>The following is a point-by-point discussion of the flaws discovered in <a href="https://redrocket.club/posts/croc/" target="_blank" >RedRocket&rsquo;s blog post</a> and the fixes I developed to patch them. To start, though, I want to acknowledge that I am grateful to RedRocket for providing these security observations to me in such a precise and helpful way, and would urge anyone out there to support RedRocket and their CVE discoveries of open-source software. As pointed out by RedRocket:</p>
<blockquote>
<p>A small bug can go a long way. It is difficult to implement crypto protocols correctly, especially when a small detail can completely break all security. Unfortunately that’s the case for most protocols. Cyptography is still scary.</p>
</blockquote>
<p>I believe this to be very true, and I try to work hard that I can make sure crypto protocols work well in croc so that it remains secure.</p>
<h3 id="vulnerability-in-spake2-implementation">Vulnerability in SPAKE2 implementation</h3>
<blockquote>
<p>As described in the RFC for SPAKE2 the public identities for both parties should be hardcoded by the application. These values should be chosen in a way where the developer can prove that it is unlikely for him to know the discrete logarithm of these values as well.</p>
</blockquote>
<p>This has been fixed in <a href="https://github.com/schollz/pake/blob/master/pake.go#L76-L107" target="_blank" >pake.go</a> for croc&rsquo;s SPAKE2 implementation.</p>
<p>As RedRocket notes, my implementation of SPAKE2 did not enforce a specific value for the points along the elliptic curve. In my implementation these points were chosen at random. However, what Aaron of RedRocket cleverly figured out, was that they could choose entities for the curves with known discrete logarithms so that the produced key was much weaker (and breakable through bruteforce).</p>
<p>As suggested by RedRocket, and as described in <a href="https://datatracker.ietf.org/doc/draft-irtf-cfrg-spake2/18/" target="_blank" >the forthcoming RFC for SPAKE2</a>, the public identifies should be hardcoded and the values should be chosen in a way that I can prove its unlikely I know the discrete algorithm. So, with help from Travis Scholl, these public identities were generated using a hash of a phrase <code>croc1</code> and <code>croc2</code> to prove I don&rsquo;t know the discrete algorithm. Here is the code to verify the points: <a href="https://github.com/schollz/pake#hard-coded-elliptic-curve-points" target="_blank" >SageMath code to generate points</a>.</p>
<h3 id="siec-use">SIEC use</h3>
<blockquote>
<p>Unfortunately, croc uses a very unoptimized implementation of an uncommon cofactor-one curve called siec. So for our exploit to work in a reasonable time, we either had to properly implement the curve ourselves or throw more computational power at it. Out of convinience we took the latter option and spun up 10 AWS instances as bruteforce workers.</p>
</blockquote>
<p>croc by default uses a less known curve called SIEC. It belongs to a class of curves called &ldquo;super-isolated&rdquo; and were first introduced by <a href="https://doi.org/10.1080/10586458.2017.1412371" target="_blank" >Travis Scholl</a>. Being super-isolated means it is more difficult to transfer the discrete log problem (the basis of security for classical elliptic curve cryptosystems) to another curve. Hence the security of a SIEC curve does not reduce to the security of the curves around it. These curves necessarily have a small conductor (similar to the curve Bitcoin uses Secp256k1) , and usually admit low degree endomorphisms which could be used to speed up point addition using the well-known <a href="https://www.iacr.org/archive/eurocrypt2009/54790519/54790519.pdf" target="_blank" >GLV/GLS method</a>. Some believe curves with &ldquo;special features&rdquo; are <a href="https://safecurves.cr.yp.to/" target="_blank" >less secure</a>. But there are cases where more general objects are less secure, like for <a href="https://link.springer.com/article/10.1007/s00145-007-9014-6" target="_blank" >genus 3 curves</a>. Because SIEC has not had the popularity of other curves, it has not received the same careful hand optimizing other curves had like <a href="https://groups.google.com/g/golang-codereviews/c/m5QTnSUZU6c" target="_blank" >secp256k1</a>, <a href="https://github.com/bitcoin-core/secp256k1" target="_blank" >P-256</a>, and <a href="https://github.com/msotoodeh/curve25519" target="_blank" >ec25519</a>. But there is no reason that a similar effort could be made to significantly speed up curve operations. And for those who would rather use a standard curve, it is a minor change to make configurable.</p>
<p>Anyways, the RedRocket team says &ldquo;unfortunately&rdquo; here which I take to mean that it was unfortunate for them to have to spin up AWS computers since the siec implementation is less optimized than others - not that the curve itself is unfortunate for cryptography (which I believe is not, as outlined above).</p>
<h3 id="sender-and-recipient-should-not-exchange-bcrypt-hash-of-key">Sender and recipient should not exchange bcrypt hash of key</h3>
<blockquote>
<p>The sender then answers with its public parameter YYY as well as HkbH_{kb}Hkb​, which is a bcrypt hash of the exchanged key. This is an absolutely terrible idea and already enough to brute force the exchanged key. Unfortunately, bcrypt is used for hashing, which is computationally expensive. With our resources we would need at least a few days to brute force the the passcode based on this hash&hellip;.Also there should be a check implemented which ensures that Hka and Hkb are different from each other.</p>
</blockquote>
<p>This has been fixed to <a href="https://github.com/schollz/croc/commit/6be4080f0569b53404c9245fad78a9940e58b9f0" target="_blank" >remove any hash exchange</a> of the secure key.</p>
<p>Part of the attack described above was made possible because of a superfluous check that the secure passphrase generated from SPAKE2 are correct between the two parties. I had the same hash for each party which enabled a nefarious party to skip this check by sending back the hash it received from the other.</p>
<p>However, since I&rsquo;m using AES-GCM with the generated key, I don&rsquo;t need this check at all and it has been removed from the code. If the two parties have different keys it will be obvious when it fails the decryption, and <a href="https://golang.org/src/crypto/cipher/gcm.go?s=459:1799#L7" target="_blank" >AES-GCM</a> is able to both decrypt and authenticate the message. This also has the beneficial byproduct of being faster than the previous implementation since bcrypt is not needed anymore.</p>
<h3 id="room-leaks-first-word-of-the-passcode">Room leaks first word of the passcode</h3>
<blockquote>
<p>The room should not be derived from the passcode but rather uses a separate identifier.</p>
</blockquote>
<p>This <a href="https://github.com/schollz/croc/commit/628043b228daf50d0c6774cdcd4ea7e2b1fd42dd" target="_blank" >has been fixed</a> to add a &ldquo;pin&rdquo; that designates room so that the weak key is never used as part of the room for exchange.</p>
<p>All generated code-phrases are preceded by a 4-digit pin number that is used to identify the &ldquo;room&rdquo; for the transfer to take place. The rest of the code-phrase is used for the shared secret to generate the secure key. This will ensure there is no information about the secure passphrase generation passed between the two parties.</p>
<h3 id="pathtraversal-issues">Pathtraversal issues</h3>
<blockquote>
<p>The sender should not be able to control the path of the file. The recipient should just take the filename from the sender and should save the file to the current working directory.</p>
</blockquote>
<p>This has been fixed to <a href="https://github.com/schollz/croc/commit/be5ceae8c7dba6d783fa28ed33376f8ed5182252" target="_blank" >prompt overwrites</a>.</p>
<p>In every version of croc the sender is only able to control the path of the file within the recipient&rsquo;s current working directory. However, the ability to be informed of overwriting files is useful and I have now added it.</p>
<p>To be clear, there are two modes of file transmission in croc:</p>
<ol>
<li>Sender sends a single file. In this case, the recipient will save the file to its current directory <em>no matter where the file originated from the sender.</em> This has been the default behavior for all versions of croc.</li>
<li>Sender sends a folder. In this case, the sender <em>cannot</em> send a folder that is not in the sender&rsquo;s working directory. The recipient will receive the folder and save it and everything in it into the recipient&rsquo;s working directory.</li>
</ol>
<p>Previous versions of croc would automatically overwrite files if they were not the same (but only ever in the working directory of the recipient). I changed the default behavior to prompt for overwriting, which can be turned off with <code>-overwrite</code>. The limitation to receive files in the current working directory is not alterable.</p>
<h3 id="protocol-sequence-is-not-enforced">Protocol sequence is not enforced</h3>
<blockquote>
<p>All messages between the recipient and sender are handelt by the processMessage function in croc.go. This function does not enforce the sequence of the messages. If an encryption key is set incoming messages are decrypted. Otherwise the messages are processed without decryption. This allows an attacker to start the protocol with a external ip message and omit the key exchange to use the protocol without encryption. The only thing preventing the sender from sending the file unencrypted is the fact that the go crypto library will throw an error while encrypting data with a nil key and that will result in a panic.</p>
</blockquote>
<p>This <a href="https://github.com/schollz/croc/commit/c02b4f12560ae13f19e3c290e7df8ded686f2518" target="_blank" >has been fixed</a>.</p>
<p>In croc version 8, only the PAKE communication is unencrypted and that all other communication requires encryption. So in the now version 9, I have fixed it so that <em>only</em> PAKE communication is explicitly allowed to be unencrypted, while all other messages must be encrypted or it will be rejected with error.</p>
<h3 id="send-to-relay-security">Send-to-relay security</h3>
<blockquote>
<p>This sender-to-relay connection is “secured” with a SPAKE2 key exchange. Strangely this connection uses the hardcoded passcode pass123 and therefore does not provide security against an active attacker. Although somewhat nasty, we won’t focus on this bug here because confidentiality and authenticity are not crucial for this step.</p>
</blockquote>
<p>In this case, the <code>pass123</code> is simplistic because it is a public relay and it only exchanges the name of the room during the transmissions with the ephemeral key generated with this simple passphrase to encrypt &ldquo;over-the-wire&rdquo;.  After talking with RedRocket I&rsquo;ve realized that better security would be to additionally authenticate the public server by generating a keypair and binding the default croc client with the public key so that the public relay (and private) can be authenticated and not just encrypted via a common passphrase. This is work in progress, soon to be released in v10 (probably next week).</p>
]]></description>
    </item>
    
    <item>
	    <title>Waveforms on the web</title>
	    <image>https://schollz.com/img/webwaveform.png</image>
      <link>/tinker/waveforms/</link>
      <pubDate>Tue, 13 Apr 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/waveforms/</guid>
      <description><![CDATA[<p>In my previous post about the <a href="/blog/tone-to-drone/" >SuperCollider musical programming language</a> I wanted to include some audio demos. I didn&rsquo;t like the traditional way of showing an audio file and tried to make something more akin to how soundcloud renders (i.e. an abstract waveform). It turns out this is really easy to do thanks to <a href="https://wavesurfer-js.org/" target="_blank" ><code>wavesurfer.js</code></a>.</p>
<p>This is the traditional way of making showing an audio file in HTML:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="p">&lt;</span><span class="nt">audio</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;drone5.wav&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;audio/wav&#34;</span> <span class="na">controls</span><span class="p">&gt;&lt;/</span><span class="nt">audio</span><span class="p">&gt;</span> 
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl"><span class="p">&lt;</span><span class="nt">audio</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;drone4.wav&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;audio/wav&#34;</span> <span class="na">controls</span><span class="p">&gt;&lt;/</span><span class="nt">audio</span><span class="p">&gt;</span> 
</span></span></code></pre></div><p>which looks like this:</p>
<p><audio src="/img/sc/drone5.wav" type="audio/wav" controls></audio> <br>
<audio src="/img/sc/drone4.wav" type="audio/wav" controls></audio></p>
<p>It turns out its really easy to now get a waveform visualized playback with a drop-in replacement for the <code>audio</code> tag. With about 50 lines of code (below) you can convert <code>audio</code> elements into something much nicer and still practical.</p>
<p>Now the same two <code>audio</code> tags will instead look like this:</p>
<p><audio src="/img/sc/drone5.wav" type="audio/wav" class="waveform" controls></audio>
<audio src="/img/sc/drone4.wav" type="audio/wav" class="waveform" controls></audio></p>
<h3 id="how-does-it-work">How does it work</h3>
<p>I simpy wrote a little javascript as a wrapper around <a href="https://wavesurfer-js.org/" target="_blank" ><code>wavesurfer.js</code></a>. The wrapper looks for all the <code>audio</code> selectors and converts them to waveforms. Then it adds SVG buttons for play/pause from <a href="https://feathericons.com" target="_blank" >feathericons.com</a>.</p>
<p>Here is the entire HTML you need for a drop-in replacement to convert <code>audio</code> tags into nice playable waveforms:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/js/wavesurfer.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl">    <span class="kd">var</span> <span class="nx">els</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">&#34;audio&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">els</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">els</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">src</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl">        <span class="kd">let</span> <span class="nx">i_</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl">        <span class="kd">let</span> <span class="nx">src_</span> <span class="o">=</span> <span class="nx">els</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">src</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl">        <span class="kd">let</span> <span class="nx">newNode</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;div&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl">        <span class="nx">newNode</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="sb">`&lt;table style=&#34;padding-top:16px;padding-bottom:16px;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;position: relative;top: 4px;width:40px;&#34;&gt;&lt;div class=&#34;controls&#34; style=&#34;&#34;&gt; &lt;button class=&#34;audiobtn&#34; data-action=&#34;play&#34; style=&#34;background: #fff; border: none; padding-left:0;padding-right:0;&#34;&gt; &lt;svg class=&#34;playicon&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;40&#34; height=&#34;40&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;1&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;feather feather-play-circle&#34;&gt;&lt;circle cx=&#34;12&#34; cy=&#34;12&#34; r=&#34;10&#34;&gt;&lt;/circle&gt;&lt;polygon points=&#34;10 8 16 12 10 16 10 8&#34;&gt;&lt;/polygon&gt;&lt;/svg&gt; &lt;svg class=&#34;pauseicon&#34; style=&#34;display:none;&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;40&#34; height=&#34;40&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;1&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;feather feather-pause-circle&#34;&gt;&lt;circle cx=&#34;12&#34; cy=&#34;12&#34; r=&#34;10&#34;&gt;&lt;/circle&gt;&lt;line x1=&#34;10&#34; y1=&#34;15&#34; x2=&#34;10&#34; y2=&#34;9&#34;&gt;&lt;/line&gt;&lt;line x1=&#34;14&#34; y1=&#34;15&#34; x2=&#34;14&#34; y2=&#34;9&#34;&gt;&lt;/line&gt;&lt;/svg&gt; &lt;/button&gt;&lt;/div&gt;&lt;/td&gt;&lt;td width=&#34;100%&#34;&gt;&lt;div class=&#34;wave&#34;&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;`</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl">        <span class="nx">els</span><span class="p">[</span><span class="nx">i_</span><span class="p">].</span><span class="nx">parentNode</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">newNode</span><span class="p">,</span> <span class="nx">els</span><span class="p">[</span><span class="nx">i_</span><span class="p">])</span>
</span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl">        <span class="nx">els</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">remove</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl">        <span class="kd">let</span> <span class="nx">wavesurfer</span> <span class="o">=</span> <span class="nx">WaveSurfer</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
</span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl">            <span class="nx">container</span><span class="o">:</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s2">&#34;wave&#34;</span><span class="p">)[</span><span class="nx">i_</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl">            <span class="nx">waveColor</span><span class="o">:</span> <span class="s1">&#39;#909090&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a></span><span class="cl">            <span class="nx">progressColor</span><span class="o">:</span> <span class="s1">&#39;#443e3c&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a></span><span class="cl">            <span class="nx">cursorColor</span><span class="o">:</span> <span class="s1">&#39;#ffffff&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a></span><span class="cl">            <span class="nx">backend</span><span class="o">:</span> <span class="s1">&#39;MediaElement&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a></span><span class="cl">            <span class="nx">mediaControls</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a></span><span class="cl">            <span class="nx">hideScrollbar</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a></span><span class="cl">            <span class="nx">minPxPerSec</span><span class="o">:</span> <span class="mi">120</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a></span><span class="cl">            <span class="nx">normalize</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a></span><span class="cl">            <span class="nx">height</span><span class="o">:</span> <span class="mi">64</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-24"><a class="lnlinks" href="#hl-1-24">24</a></span><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="ln" id="hl-1-25"><a class="lnlinks" href="#hl-1-25">25</a></span><span class="cl">        <span class="nx">wavesurfer</span><span class="p">.</span><span class="nx">once</span><span class="p">(</span><span class="s1">&#39;ready&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-26"><a class="lnlinks" href="#hl-1-26">26</a></span><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Using wavesurfer.js &#39;</span> <span class="o">+</span> <span class="nx">WaveSurfer</span><span class="p">.</span><span class="nx">VERSION</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-1-27"><a class="lnlinks" href="#hl-1-27">27</a></span><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="ln" id="hl-1-28"><a class="lnlinks" href="#hl-1-28">28</a></span><span class="cl">        <span class="nx">wavesurfer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-29"><a class="lnlinks" href="#hl-1-29">29</a></span><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-1-30"><a class="lnlinks" href="#hl-1-30">30</a></span><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="ln" id="hl-1-31"><a class="lnlinks" href="#hl-1-31">31</a></span><span class="cl">        <span class="nx">wavesurfer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;play&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-32"><a class="lnlinks" href="#hl-1-32">32</a></span><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s2">&#34;playicon&#34;</span><span class="p">)[</span><span class="nx">i_</span><span class="p">].</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="s2">&#34;none&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-33"><a class="lnlinks" href="#hl-1-33">33</a></span><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s2">&#34;pauseicon&#34;</span><span class="p">)[</span><span class="nx">i_</span><span class="p">].</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="s2">&#34;block&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-34"><a class="lnlinks" href="#hl-1-34">34</a></span><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="ln" id="hl-1-35"><a class="lnlinks" href="#hl-1-35">35</a></span><span class="cl">        <span class="nx">wavesurfer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;pause&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-36"><a class="lnlinks" href="#hl-1-36">36</a></span><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s2">&#34;playicon&#34;</span><span class="p">)[</span><span class="nx">i_</span><span class="p">].</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="s2">&#34;block&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-37"><a class="lnlinks" href="#hl-1-37">37</a></span><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="s2">&#34;pauseicon&#34;</span><span class="p">)[</span><span class="nx">i_</span><span class="p">].</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="s2">&#34;none&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-38"><a class="lnlinks" href="#hl-1-38">38</a></span><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="ln" id="hl-1-39"><a class="lnlinks" href="#hl-1-39">39</a></span><span class="cl">        <span class="nx">newNode</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;[data-action=&#34;play&#34;]&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-40"><a class="lnlinks" href="#hl-1-40">40</a></span><span class="cl">            <span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="nx">wavesurfer</span><span class="p">.</span><span class="nx">playPause</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">wavesurfer</span><span class="p">));</span>
</span></span><span class="line"><span class="ln" id="hl-1-41"><a class="lnlinks" href="#hl-1-41">41</a></span><span class="cl">        <span class="nx">wavesurfer</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="nx">src_</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-1-42"><a class="lnlinks" href="#hl-1-42">42</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-43"><a class="lnlinks" href="#hl-1-43">43</a></span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln" id="hl-1-44"><a class="lnlinks" href="#hl-1-44">44</a></span><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
]]></description>
    </item>
    
    <item>
	    <title>Banana peel</title>
	    <image>https://schollz.com/img/newyorker/banana.png</image>
      <link>/newyorker/banana-peel/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/banana-peel/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/banana.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Become the cube</title>
	    <image>https://schollz.com/img/newyorker/rubiks.png</image>
      <link>/newyorker/become-the-cube/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/become-the-cube/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/rubiks.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Cowbell</title>
	    <image>https://schollz.com/img/newyorker/cowbell.png</image>
      <link>/newyorker/cowbell/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/cowbell/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/cowbell.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Diamond</title>
	    <image>https://schollz.com/img/newyorker/music.png</image>
      <link>/newyorker/diamond/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/diamond/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/music.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Dog walking</title>
	    <image>https://schollz.com/img/newyorker/dogwalking.jpg</image>
      <link>/newyorker/dog-walking/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/dog-walking/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/dogwalking.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Eating</title>
	    <image>https://schollz.com/img/newyorker/eating.png</image>
      <link>/newyorker/eating/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/eating/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/eating.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Hourly</title>
	    <image>https://schollz.com/img/newyorker/hourly.png</image>
      <link>/newyorker/hourly/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/hourly/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/hourly.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Juliard</title>
	    <image>https://schollz.com/img/newyorker/phonto.png</image>
      <link>/newyorker/juliard/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/juliard/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/phonto.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Makeshift</title>
	    <image>https://schollz.com/img/newyorker/makeshift.png</image>
      <link>/newyorker/makeshift/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/makeshift/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/makeshift.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Origami</title>
	    <image>https://schollz.com/img/newyorker/origami.png</image>
      <link>/newyorker/origami/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/origami/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/origami.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Pitch</title>
	    <image>https://schollz.com/img/newyorker/elevator.jpg</image>
      <link>/newyorker/pitch/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/pitch/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/elevator.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Plants</title>
	    <image>https://schollz.com/img/newyorker/plant.png</image>
      <link>/newyorker/plants/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/plants/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/plant.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Rock paper</title>
	    <image>https://schollz.com/img/newyorker/rockpaper.png</image>
      <link>/newyorker/rock-paper/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/rock-paper/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 5/27/2020.</p>
<p>Declined by The New Yorker on 02/25/2021.</p>
<p><img src="/img/newyorker/rockpaper.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Symphony</title>
	    <image>https://schollz.com/img/newyorker/symphony.JPG</image>
      <link>/newyorker/symphony/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/symphony/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/symphony.JPG" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Trainer</title>
	    <image>https://schollz.com/img/newyorker/pushups.jpg</image>
      <link>/newyorker/trainer/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/trainer/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/pushups.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Trick</title>
	    <image>https://schollz.com/img/newyorker/trick.png</image>
      <link>/newyorker/trick/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/trick/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/trick.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>You call that</title>
	    <image>https://schollz.com/img/newyorker/rubiks2.png</image>
      <link>/newyorker/you-call-that/</link>
      <pubDate>Mon, 12 Apr 2021 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/you-call-that/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 3/29/2020.</p>
<p>Declined by The New Yorker on 01/27/2021.</p>
<p><img src="/img/newyorker/rubiks2.png" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Icarus</title>
	    <image>https://schollz.com/img/icarus.png</image>
      <link>/tinker/icarus/</link>
      <pubDate>Thu, 25 Mar 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/icarus/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/528900768" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/546103263" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<blockquote>
<p><em>“I warn you, Icarus, fly a middle course: Don&rsquo;t go too low, or water will weigh the wings down; Don&rsquo;t go too high, or the sun&rsquo;s fire will burn them. Keep to the middle way.</em> -  Daedalus</p>
</blockquote>
<p>for awhile I&rsquo;ve been attempting to make a synth emulating the korg monotron delay, one of my favorite synths. I&rsquo;m using the monotron on my current album a lot and wanted to make a softsynth of it which would be a little easier to use in my other scripts. this is a synth for norns that is close to what I envisioned, with some cool features not present in the monotron (pitch stability) but also still missing some of the intense time-swooping (I hope to get there soon).</p>
<p>the synth is presented on the norns with three controls shown in the landscape. the sun shows feedback and levels, the water shows filter and the position shows time. i recommend interacting with these facets in realtime to contour the sound. like icarus, you can keep to the middle, you can get burned up by the sun, or you can get swallowed up by the sea.</p>
<h2 id="requirements">requirements</h2>
<ul>
<li>norns</li>
<li>midi keyboard</li>
</ul>
<h2 id="documentation">documentation</h2>
<p>(plug in midi keyboard first)</p>
<ul>
<li>E1 = time</li>
<li>E2 = filter</li>
<li>E3 = feedback</li>
<li>K1/2/3 does a bounce on those three</li>
</ul>
<p>moving time forward causes the sun to more easily collapse.</p>
<p>lots of parameters in the menu.</p>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/icarus
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Plonky</title>
	    <image>https://schollz.com/img/plonky.png</image>
      <link>/tinker/plonky/</link>
      <pubDate>Sun, 07 Mar 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/plonky/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/520650445" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<blockquote>
<p>plonk (/plɒŋk/) - to play a musical instrument, usually not very well but often loudly - Cambride Dictionary</p>
</blockquote>
<p>plonky is a keyboard and sequencer. i made it to be able to play <a href="https://llllllll.co/t/mx-samples/41400" target="_blank" >mx.samples</a> directly from the grid. the grid layout (and name) is inspired by the <a href="https://www.plinkysynth.com/" target="_blank" >plinky synth</a>, i.e. it is a 8x8 layout with notes spaced out between columns by a specified interval (default is C-major scale spaced out by fifths).</p>
<h3 id="requirements">Requirements</h3>
<ul>
<li>norns</li>
<li>grid (optional)</li>
</ul>
<h3 id="documentation">Documentation</h3>
<p>use the grid to play an engine. by default the engine is &ldquo;PolyPerc&rdquo;, but if you install <a href="https://llllllll.co/t/mx-samples/41400" target="_blank" >mx.samples</a> you can also play that by switching &ldquo;<code>PLONKY &gt; engine&quot;</code> via parameters.</p>
<p>each 8x8 section of the grid is a voice (1 voice for 64-grid, 2 voices for 128-grid). you can play notes in that voice by pressing pads. the notes correspond to a C-major scale, where each column is a fifth apart (all adjustable in menu).</p>
<p><strong>arps:</strong> you can do arps by turning E2 or E3 to the right. in &ldquo;arp&rdquo; mode you can press multiple keys and have them play. in &ldquo;arp+latch&rdquo; mode the last keys you pressed will play. change the speed using the &ldquo;<code>PLONKY &gt; division</code>&rdquo; parameter in the menu.</p>
<p><strong>patterns:</strong> you can record patterns by pressing K1+K2 (for voice 2 press K1+K3). press a note (or multiple) and it will become a new step in the pattern. you can hold out a step by holding the notes and pressing K2 (for voice 2 press K3). you can add a rest by releasing notes and pressing K2 (for voice 2 press K3). when done recording press K1+K2 (for voice 2 press K3). to play a pattern press K2 (for voice 2 press K3).</p>
<p><strong>midi keyboard support</strong>: every voice now has a &ldquo;midi-in&rdquo; which you can assign to it a midi keyboard for input. the midi-in keyboards only work if the voice is selected (i.e. only two work at a time). <em>note</em>: its important to note that the midi keyboard support is basically allowing to use a midi keyboard as a input into <em>plonky</em> - emphasis on plonky. meaning, plonky doesn&rsquo;t accept notes from a midi keyboard that aren&rsquo;t in the plonky grid area (even if there is no grid attached). so if you have a C major scale plugged in, plonky will only respond to the white keys on your keyboard. also, a grid only lets you play two voices at once, so similarly plonky will only respond to two midi keyboards at the same time (you can switch voices to switch which keyboards are used if they are assigned across each voice). I realize these are weird limitations, but its the only way I can see forward to keep the grid-based plonky and the now keyboard-based plonky in unison.</p>
<h3 id="install">Install</h3>
<p><a href="https://github.com/schollz/plonky" target="_blank" >https://github.com/schollz/plonky</a></p>
<p>from maiden:</p>
<pre tabindex="0"><code>;install https://github.com/schollz/plonky
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Granchild</title>
	    <image>https://schollz.com/img/granchild.png</image>
      <link>/tinker/granchild/</link>
      <pubDate>Sat, 20 Feb 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/granchild/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/576432638" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>granchild is a granular grandchild. this script was born out of and inspired by @cfd90&rsquo;s clever <a href="https://llllllll.co/t/twine-random-granulator/41703" target="_blank" >twine</a> and @justmat&rsquo;s inspiring <a href="https://llllllll.co/t/mangl/21066/307" target="_blank" >mangl</a>, both themselves based on @artfwo&rsquo;s amazing <a href="https://llllllll.co/t/glut/21175" target="_blank" >glut</a> script, which itself was inspired by @kasperskov&rsquo;s <a href="https://llllllll.co/t/grainfields-8-voice-granular-synthesizer-for-128-grids-m4l-update/5164" target="_blank" >grainfields</a>. i consider this script to be a grandchild of glut, and child of the other two - using some ideas from twine (randomization in parameters) and some code from mangl (lfos, greyhole integration) and the engine of glut, to make a granular sequencer to fit my personal musical journey.</p>
<p>their are a lot of granulation scripts based on glut, here&rsquo;s what&rsquo;s different about <em>granchild</em>:</p>
<ul>
<li>&ldquo;jitter&rdquo; and &ldquo;spread&rdquo; of granulation always oscillate, with random frequencies</li>
<li>&ldquo;size&rdquo; and &ldquo;density&rdquo; of granulation are quantized, and easily accessed (see layout)</li>
<li>sequencer is quantized (using @tyleretters&rsquo;s lattice)</li>
<li>voices limited to only 4</li>
<li>samples mapped to 18 keys</li>
<li>can record live input</li>
<li>granulation can utilize stereo samples</li>
<li>each voice has two scenes that can be toggled between</li>
</ul>
<h2 id="requirements">Requirements</h2>
<ul>
<li>norns</li>
<li>grid optional</li>
</ul>
<h2 id="documentation">Documentation</h2>
<p>here&rsquo;s how the buttons are laid out. use e2 and e3 on the norns to move around and k2/k3 to toggle things. the grid just mirrors the norns screen, so this script works just fine without a grid. <em>grid 64 users:</em> hold k1 to switch between the the first and second pair of voices.</p>
<p><img src="https://user-images.githubusercontent.com/6550035/109594838-4daa8200-7ac8-11eb-8571-51291b08195b.jpg" alt="granchild_grid-01"></p>
<p><em>note:</em> when you use the record button for live input, all recordings are automatically saved permanently in <code>~/dust/audio/granchild</code>.</p>
<h2 id="old-versions">Old versions</h2>
<p>see <a href="https://github.com/schollz/granchild#old-versions" target="_blank" >github</a></p>
<h2 id="install">Install</h2>
<p>from maiden:</p>
<pre tabindex="0"><code>;install https://github.com/schollz/granchild
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Mx. Samples</title>
	    <image>https://schollz.com/img/mx-samples.png</image>
      <link>/tinker/mxsamples/</link>
      <pubDate>Sun, 07 Feb 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/mxsamples/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/509450523" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>us as a keyboard instrument or in another script. <em>mx.samples</em> provides an accessible way to utilize the vast trove of free instrument sample libraries.  for instance, you can load in a piano that has been sampled on multiple dynamics and variations and key releases.</p>
<p>the core of the script + supercollider engine handles the voices and the instruments. you can have unlimited samples on disk because samples are loaded dynamically - only loading into memory when needed (max 200 samples can be loaded though). no latency on loading because it will load in the background (and use the closest pitched sample in the meantime).</p>
<p>my motivation for this script was to have a really nice sounding piano on the norns. also i wanted to be able to have a library of samples that works well with <a href="https://llllllll.co/t/tmi/40818" target="_blank" >tmi</a> (see <code>studies/study1.lua</code> and demo above) but can also be used for future projects in the idea galaxy.</p>
<p>a massive thank you to @zebra for helping me post-process the UofI piano samples - there is a great <a href="https://github.com/schollz/mx.samples/blob/main/samples/depop.py" target="_blank" >script available to &ldquo;de-pop&rdquo;</a> samples that had glitches in recording, thanks to that work. also this project wouldn&rsquo;t be possible without the generosity of the folks submitting their samples to be used freely at <a href="https://www.pianobook.co.uk/" target="_blank" >pianobook.co.uk</a> and at the <a href="http://theremin.music.uiowa.edu/MIS.html" target="_blank" >University of Iowa Electronic Music department</a>.</p>
<h2 id="requirements">requirements</h2>
<ul>
<li>norns</li>
<li>midi controller OR use in a script</li>
</ul>
<h2 id="documentation">documentation</h2>
<p>mx.samples can be used as a keyboard (selecting sound+parameters via menus) or as a library (selecting sound+parameters via code).</p>
<h3 id="as-a-keyboard">as a keyboard</h3>
<p>you can use <em>mx.samples</em> as a keyboard. just plugin your midi keyboard, open the script and choose a sample. samples are available to download (processed by me, you can process your own too - see below).</p>
<p>there are a bunch of effects (filters / delay) and options (tuning, down-sampling, playing releases, velocity scaling) available from the parameters menu <code>PARAMETERS -&gt; MX.SAMPLES</code>.</p>
<p><em>&ldquo;warming&rdquo; up the keyboard:</em> the very first note you play will not &ldquo;play&rdquo; (known bug) because it is loading the sample. every subsequent <em>new</em> note will re-pitch the loaded sample <em>or it will load in a sample for that note</em> to be used the next time (so no latency from load). this means that you can get the best sound by playing the notes you want to play once before you play them.</p>
<h3 id="as-a-library">as a library</h3>
<p>you can use <em>mx.samples</em> as a lua library for your project (see demo above). the api is pretty simple to include this into another script if you want a bunch of different voices. (another goal here - to use with <a href="https://llllllll.co/t/tmi/" target="_blank" >tmi</a>). instruments are loaded dynamically so you can add as many as you want.</p>
<p>see <a href="https://github.com/schollz/mx.samples/blob/main/studies/study1.lua" target="_blank" >study1.lua</a> for an example that uses <a href="https://llllllll.co/t/tmi/" target="_blank" >tmi</a> to do the sequencing.</p>
<p>basically the syntax would be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="n">mxsamples</span><span class="o">=</span><span class="n">include</span><span class="p">(</span><span class="s2">&#34;mx.samples/lib/mx.samples&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="n">engine.name</span><span class="o">=</span><span class="s2">&#34;MxSamples&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="n">skeys</span><span class="o">=</span><span class="n">mxsamples</span><span class="p">:</span><span class="n">new</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl"><span class="c1">-- play an instrument</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl"><span class="n">skeys</span><span class="p">:</span><span class="n">on</span><span class="p">({</span><span class="n">name</span><span class="o">=</span><span class="s2">&#34;ghost piano&#34;</span><span class="p">,</span><span class="n">midi</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span><span class="n">velocity</span><span class="o">=</span><span class="mi">120</span><span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl"><span class="n">skeys</span><span class="p">:</span><span class="n">on</span><span class="p">({</span><span class="n">name</span><span class="o">=</span><span class="s2">&#34;box violin&#34;</span><span class="p">,</span><span class="n">midi</span><span class="o">=</span><span class="mi">42</span><span class="p">,</span><span class="n">velocity</span><span class="o">=</span><span class="mi">120</span><span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl"><span class="c1">-- turn them off</span>
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl"><span class="n">skeys</span><span class="p">:</span><span class="n">off</span><span class="p">({</span><span class="n">name</span><span class="o">=</span><span class="s2">&#34;ghost piano&#34;</span><span class="p">,</span><span class="n">midi</span><span class="o">=</span><span class="mi">60</span><span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl"><span class="n">skeys</span><span class="p">:</span><span class="n">off</span><span class="p">({</span><span class="n">name</span><span class="o">=</span><span class="s2">&#34;box violin&#34;</span><span class="p">,</span><span class="n">midi</span><span class="o">=</span><span class="mi">42</span><span class="p">})</span>
</span></span></code></pre></div><p>the parameters can be added into the <code>on</code> function as well:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="n">skeys</span><span class="p">:</span><span class="n">on</span><span class="p">({</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl">	<span class="n">name</span><span class="o">=</span><span class="s2">&#34;ghost piano&#34;</span><span class="p">,</span><span class="n">midi</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span><span class="n">velocity</span><span class="o">=</span><span class="mi">120</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl">	<span class="n">attack</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a></span><span class="cl">	<span class="n">release</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5">5</a></span><span class="cl">	<span class="c1">-- every parameter in the menu is available here</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6">6</a></span><span class="cl"><span class="p">})</span>
</span></span></code></pre></div><h3 id="getting-samples">getting samples</h3>
<p>this script will allow you to download samples that i&rsquo;ve already processed. in theory you can use any kontakt / vst sample pack if you have access to the raw audio. in the <code>samples/</code> folder there is a utility script <code>convert.py</code> that is specific to each type of sample and shows basically how its done (its easy). here&rsquo;s <a href="https://github.com/schollz/mx.samples/blob/main/samples/steinway_model_b/convert.py" target="_blank" >an example for the UofI piano</a>.</p>
<p>the current samples are from the following sources, which are free and do not restrict to distributing them for this purpose:</p>
<ul>
<li>The University of Iowa Musical Instrument Samples database which <a href="http://theremin.music.uiowa.edu/MIS.html" target="_blank" >&ldquo;may be downloaded and used for any projects, without restrictions&rdquo;</a>.</li>
<li>the pianobook which states that <a href="https://www.pianobook.co.uk/faq/" target="_blank" >&ldquo;There are NO restrictions on their use (except selling them on as your own samples)&rdquo;</a></li>
</ul>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/mx.samples
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Monotron hack</title>
	    <image>https://schollz.com/img/tinyhacks/monotron.png</image>
      <link>/tinker/monotron/</link>
      <pubDate>Wed, 03 Feb 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/monotron/</guid>
      <description><![CDATA[<p>The Korg Monotron is a good sounding synthesizer for a relatively cheap price (~$50). One drawback of the Korg Monotron is that it uses a <a href="https://www.sweetwater.com/insync/ribbon-controller/" target="_blank" >ribbon controller</a> to determine the pitch so it is very hard to control the pitch accurately, and almost impossible to be in tune by yourself. In a previous project <a href="/raspberrypi/monotron/" >I developed a MIDI-to-CV controller using a Raspberry Pi</a>. In this <strong>tiny hack</strong> I focus on making the Monotron play by itself again - by being always on and <em>droning</em>. The pitch of the drone can be easily adjusted and you can then use it for great effect!</p>
<h2 id="what-does-this-tiny-hack-do">What does this <em>tiny hack</em> do?</h2>
<p>Here&rsquo;s a demo of the tiny hack! Basically this hack allows me to easily play the Monotron as drone while playing other synthesizers. In this demo I&rsquo;m having the Monotron drone on a low C, while I play chords on top of it!</p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/590605474" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<h2 id="why-do-this-tiny-hack">Why do this tiny hack?</h2>
<p>The Korg Monotron usually don&rsquo;t turn &ldquo;on&rdquo; until you press the ribbon keyboard. So if you want the Monotron to drone on one note you&rsquo;d have to press your finger down on it and leave your finger there. Using this hack you can replace your finger with a potentiometer which always keeps it &ldquo;on&rdquo; and also sets the pitch to whatever you want! Its still manually controlled, but now using a pot instead of a ribbon.</p>
<h2 id="how-do-i-do-this-tiny-hack">How do I do this tiny hack?</h2>
<p>If you already have some diodes and resistors and a potentiometer, this hack will cost you nothing. Otherwise you can get those basic utilities:</p>
<ul>
<li><a href="https://www.amazon.com/gp/product/B00684KFAM/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B00684KFAM&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Korg Monotron</a> (~$55)</li>
<li><a href="https://www.amazon.com/gp/product/B01A0VH5L0/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B01A0VH5L0&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >10k potentiometer</a></li>
<li><a href="https://www.amazon.com/gp/product/B07QJB31M7/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07QJB31M7&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >10k resistor</a></li>
<li><a href="https://www.amazon.com/gp/product/B008UFWHL2/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B008UFWHL2&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Diodes</a></li>
<li><a href="https://www.amazon.com/gp/product/B07LFD4LT6/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07LFD4LT6&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Breadboard</a></li>
<li><a href="https://www.amazon.com/gp/product/B07GTGGLXN/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07GTGGLXN&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Soldering iron</a></li>
</ul>
<p>To connect it you will first need to solder wires to the back of the Korg Monotron. Unscrew the Korg Monotron and attach three wires to the pads labeled &ldquo;VCC&rdquo;, &ldquo;GND&rdquo;, and &ldquo;GATE&rdquo; (yes &ldquo;PITCH&rdquo; is a pad, but we will not use it).</p>
<p><img src="/img/tinyhacks/monocircuit.jpeg" alt="Back of Korg Monotron, with connections circled"></p>
<p>Then you can build a simple circuit using the resistor, potentiometer and the diodes.</p>
<p><img src="/img/tinyhacks/monoschematic.jpeg" alt="Circuit diagram"></p>
<p><em>Why does it work?</em> Basically the diodes are there to drop the voltage of Monotron (which runs &gt; 4V) to the highest voltage of a pitch (~3 V). Then the potentiometer and resistor form a voltage divider that divides the voltage even further proportional to turning it. This way the knob turns between allowable notes (low notes to the left and high notes to the right).</p>
<h2 id="uh-oh-something-bad-happened">Uh oh something bad happened!</h2>
<p>Oh no! I&rsquo;m happy to help - just DM me on Twitter (<a href="https://twitter.com/yakczar" target="_blank" >@yakczar</a>) or email me.</p>
]]></description>
    </item>
    
    <item>
	    <title>SuperCollider synthesis</title>
	    <image>https://schollz.com/img/drone.jpg</image>
      <link>/tinker/tone-to-drone/</link>
      <pubDate>Mon, 01 Feb 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/tone-to-drone/</guid>
      <description><![CDATA[<p><em>Note: cover image from <a href="https://www.ajournalofmusicalthings.com/fourth-annual-drone-music-day-today/" target="_blank" >https://www.ajournalofmusicalthings.com/fourth-annual-drone-music-day-today/</a></em></p>
<p><a href="https://supercollider.github.io/" target="_blank" >SuperCollider</a> is a free platform for audio synthesis and algorithm composition. It really excels at real-time analysis, synthesis and processing and provides a framework for seamless combination of audio techniques like additive and subtractive synthesis. The following is a tutorial to learn how to make <a href="https://en.wikipedia.org/wiki/Drone_music" target="_blank" >a drone</a> in SuperCollider (e.g. Sunn O))))<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>)</p>
<h2 id="before-you-begin">Before you begin</h2>
<p>Before starting, make sure you have SuperCollider and plugins (if you want).</p>
<ul>
<li><a href="https://supercollider.github.io/download" target="_blank" >Install SuperCollider</a></li>
<li><a href="https://supercollider.github.io/sc3-plugins/" target="_blank" >Install SuperCollider plugins</a> (optional, but recommended)</li>
</ul>
<p>If you have these basic things installed, you are good to go. Feel free to do a SuperCollider tutorial if you want, but I&rsquo;ll assume you don&rsquo;t know how to use it.</p>
<h2 id="five-steps-to-get-in-the-drone-zone">Five steps to get in the drone zone</h2>
<p>We are going to make the drone I call &ldquo;<strong>Dreamcrusher</strong>&rdquo;, a primal sawtooth drone. We&rsquo;ll get there from a simple sine wave, in just 5 simple steps.</p>
<h4 id="dreamcrusher-final-version">Dreamcrusher (final version):</h4>
<p><audio src="/img/sc/drone5.mp3" class="waveform"></audio></p>
<h3 id="1-tone">1. Tone</h3>
<p>We&rsquo;ll start with a basic tone and turn it into a <em>drone</em>.
First open SuperCollider and &ldquo;boot the server&rdquo; by selecting <code>Server -&gt; Boot Server</code> from the menu.</p>
<p>Now paste the following code as the starting drone. this drone is simply a C3 sine wave (C2 = 131.81 hz).</p>
<h4 id="dreamcrusher-version-1">Dreamcrusher (version 1)</h4>
<p><audio src="/img/sc/drone1.mp3" class="waveform"></audio></p>
<h4 id="code-version-1">Code (version 1)</h4>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x = SynthDef(&#34;basic&#34;,
    {
        arg hz=131.81, amp=0.5;
        var sig; // define variables we need
        
        // the drone zone!
        sig = SinOsc.ar(
            freq:hz,
            mul:amp,
        );
        
        // make two versions, one for left and one for right
        sig = sig.dup;
        
        // make sound!
        Out.ar(0,sig);
    }
).play;
)
</code></pre><p>To run this code, click your cursor anywhere in the code that you just pasted and press <code>Ctl+&lt;enter&gt;</code>. This runs all the code between the first and last parentheses. You should hear a very low sound (maybe use headphones to hear it!).</p>
<p>To stop it just press <code>Ctl+&lt;period&gt;</code>.</p>
<p>Another cool trick is that you can change things while its playing, for instance we can change the frequency and volume (use <code>Ctl+Enter</code> to run each line).</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">x.set(&#34;hz&#34;,65.41*2); // up can octave
x.set(&#34;amp&#34;,0.01); // volume down
</code></pre><p>The reason this works is that we defined the drone into the <code>x</code> variable and defined arguments (see the line &ldquo;<code>arg hz=65.41, amp=0.5;</code>&rdquo;).</p>
<h3 id="2-add-overtones">2. Add overtones</h3>
<p>Mow lets make it less boring with overtones. Overtones are multiples of fundamental at lower volumes.  We can easily just repeat what we wrote before but wrap it inside a <code>Mix.ar()</code> function in an array (denoted by the brackets).</p>
<h4 id="dreamcrusher-version-2">Dreamcrusher (version 2)</h4>
<p><audio src="/img/sc/drone2.mp3" class="waveform"></audio></p>
<h4 id="code-version-2">Code (version 2)</h4>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x = SynthDef(&#34;basic_w_overtones&#34;,
    {
        arg hz=131.81, amp=0.5;
        var sig;

        // the drone zone!
        sig = Mix.ar([
            SinOsc.ar(
                freq: hz,
                mul: amp,
            ),
            SinOsc.ar(
                freq: hz*2,
                mul: amp/2,
            ),
            SinOsc.ar(
                freq: hz*4,
                mul: amp/4,
            )
        ]);

        // spread the signal
        sig = Splay.ar(sig);

        // make sound!
        Out.ar(0,sig);
    }
).play;
)
</code></pre><p>I made sure to decrease the volume of each increasing frequency using by dividing the <code>amp</code> by a multiple of 2.</p>
<h3 id="3-different-oscillators">3. Different oscillators!</h3>
<p>Sine waves are nice, but there are lots of different oscillators we can use. we can swap out <a href="https://doc.sccode.org/Classes/SinOsc.html" target="_blank" ><code>SinOsc.ar</code></a> (sine wave) for <a href="https://doc.sccode.org/Classes/VarSaw.html" target="_blank" ><code>VarSaw</code></a> (sawtooth), <a href="https://doc.sccode.org/Classes/SinOscFB.html" target="_blank" ><code>SinOscFB</code></a> (sine w/ feedback), <a href="https://doc.sccode.org/Classes/Pulse.html" target="_blank" ><code>Pulse</code></a>, or many others. Here I&rsquo;ve opted for the saw wave:</p>
<h4 id="dreamcrusher-version-3">Dreamcrusher (version 3)</h4>
<p><audio src="/img/sc/drone3.mp3" class="waveform"></audio></p>
<h4 id="code-version-3">Code (version 3)</h4>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x = SynthDef(&#34;basic_w_overtones_varsaw&#34;,
    {
        arg hz=131.81, amp=0.5;
        var sig;

        // the drone zone!
        sig = Mix.ar([
            VarSaw.ar(
                freq: hz,
                mul: amp,
            ),
            VarSaw.ar(
                freq: hz*2,
                mul: amp/2,
            ),
            VarSaw.ar(
                freq: hz*4,
                mul: amp/4,
            )
        ]);

        // spread the signal
        sig = Splay.ar(sig);

        // make sound!
        Out.ar(0,sig);
    }
).play;
)
</code></pre><p>Try other oscillators! See what happens.</p>
<h3 id="4-add-modulation">4. Add modulation</h3>
<p>Modulation will bring in organic wavering to the drone. There are many ways to do this, but my favorite is to use a randomly oscillating sine wave with a long period.</p>
<p>We can get randomness all sorts of different ways to get modulation.</p>
<h4 id="stepped-randomness">Stepped randomness</h4>
<p>Using the <a href="https://doc.sccode.org/Classes/LFNoise0.html" target="_blank" ><code>LFnoise0.kr</code></a>:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">{LFNoise0.kr(freq:10)}.plot(1)
</code></pre><p><img src="/img/sc/lfnoise0.png" alt="LFNoise0.kr(freq:10)"></p>
<h4 id="oscillating-randomness">Oscillating randomness</h4>
<p>Then we can make a sine wave which uses that stepped noise as its frequency:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">{SinOsc.kr(freq:LFNoise0.kr(freq:2)*4)}.plot(4)
</code></pre><p><img src="/img/sc/sinosclfnoise.png" alt="SinOsc.kr(LFNoise0.kr(2),mul:4)"></p>
<p>The sine wave naturally has values between -1 and 1, so we might need to change that. its easy to change to any range you want with the <a href="https://doc.sccode.org/Classes/UGen.html#-range" target="_blank" ><code>.range(min,max)</code></a> function (or by using <a href="https://doc.sccode.org/Classes/LinLin.html" target="_blank" ><code>LinLin.kr</code></a>). Here i can change it to 0-100:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">{SinOsc.kr(freq:LFNoise0.kr(freq:1)).range(0,100)}.plot(4)
</code></pre><h4 id="drunken-walk">Drunken walk</h4>
<p>You can dream up any sort of modulation that you like. another of my favorite modulation sources of is a nice &ldquo;drunken walk&rdquo; (best to replace both 5&rsquo;s by the number you want):</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">{VarLag.kr(LFNoise0.kr(5).range(0,100),1/5,warp:\sine)}.plot(4)
</code></pre><p><img src="/img/sc/varlag.png" alt="VarLag.kr(LFNoise0.kr(5).range(0,100),1/5,warp:\sine)"></p>
<p>You can easily swap in and out these modulations, just make sure to convert the <code>.range(min,max)</code> to the range you need it.</p>
<p>Now throw those into the drone to modulate the frequencies and the width (a parameter associated with the <code>VarSaw</code> ) of our oscillator:</p>
<h4 id="dreamcrusher-version-4">Dreamcrusher (version 4)</h4>
<p><audio src="/img/sc/drone4.mp3" class="waveform"></audio></p>
<h4 id="code-version-4">Code (version 4)</h4>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x = SynthDef(&#34;basic_w_overtones_varsaw_modulation&#34;,
    {
        arg hz=131.81, amp=0.5;
        var sig;
        
        // the drone zone!
        sig = Mix.ar(
            VarSaw.ar(
                freq: Lag.kr(hz * SinOsc.kr(LFNoise0.kr(1)).range(0.99,1.01),1),
                width: SinOsc.kr(LFNoise0.kr(1)).range(0.4,0.6),
                mul: amp ,
            ) +
            VarSaw.ar(
                freq: Lag.kr(2*hz * SinOsc.kr(LFNoise0.kr(1)).range(0.99,1.01),1),
                width: SinOsc.kr(LFNoise0.kr(1)).range(0.4,0.6),
                mul: amp/2,
            ) +
            VarSaw.ar(
                freq: Lag.kr(4*hz * SinOsc.kr(LFNoise0.kr(1)).range(0.99,1.01),1),
                width: SinOsc.kr(LFNoise0.kr(1)).range(0.4,0.6),
                mul: amp/4,
            )
        );
        
        // spread the signal
        sig = Splay.ar(sig);
        
        // pan
        sig = Balance2.ar(sig[0] ,sig[1],SinOsc.kr(
            LFNoise0.kr(0.1).range(0.05,0.2)
        )*0.1);
        
        // make sound!
        Out.ar(0,sig);
    }
).play;
)
</code></pre><p>Also note, at the end I threw in more modulation in the stereo field - by modulating the <em>pan</em> of the signal with the <a href="https://doc.sccode.org/Classes/Balance2.html" target="_blank" ><code>Balance2</code></a> function which handles panning of the signal.</p>
<p>Getting to be pretty drone-like!</p>
<h3 id="5-add-filter--effects">5. Add filter + effects</h3>
<p>Filters and effects go a long way. There are lots of filters available: <a href="https://doc.sccode.org/Classes/BPF.html" target="_blank" ><code>BPF</code></a> (band pass), <a href="https://doc.sccode.org/Classes/LPF.html" target="_blank" ><code>LPF</code></a> (low pass), <a href="https://doc.sccode.org/Classes/MoogFF.html" target="_blank" ><code>MoogFF</code></a> (Moog VCF), <a href="https://doc.sccode.org/Classes/HPF.html" target="_blank" ><code>HPF</code></a> (high pass), to name a few. there are also some nice effects available like <a href="https://doc.sccode.org/Classes/FreeVerb.html" target="_blank" ><code>FreeVerb</code></a> and <a href="https://doc.sccode.org/Classes/Greyhole.html" target="_blank" ><code>Greyhole</code></a> (extensions needed for latter).</p>
<p>You can cruise the documentation and the examples in the documentation to get little snippets that you can easily reuse. The final drone iteration, which I call &ldquo;Dreamcrusher&rdquo; came out by looking at the <a href="https://doc.sccode.org/Classes/LocalOut.html" target="_blank" ><code>LocalOut</code></a> docs and pulling a little feedback example and inserting it, along with feedback into the drone above. Of course I added the <a href="#drunken-walk" >drunken walk</a> type modulation to both. And voila! We are drone.</p>
<h4 id="drone-final-version">Drone (final version)</h4>
<p><audio src="/img/sc/drone5.mp3" class="waveform"></audio></p>
<h4 id="code-final-version">Code (final version)</h4>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">(
x = SynthDef(&#34;dreamcrusher&#34;,
    {
        arg hz=131.81, amp=0.5;
        var local, ampcheck, sig;

        // the oscillator part
        sig = Mix.ar(
            VarSaw.ar(
                freq: Lag.kr(hz * SinOsc.kr(LFNoise0.kr(1)).range(0.99,1.01),1),
                width: SinOsc.kr(LFNoise0.kr(1)).range(0.4,0.6),
                mul: amp,
            ) +
            VarSaw.ar(
                freq: Lag.kr(2*hz * SinOsc.kr(LFNoise0.kr(1)).range(0.99,1.01),1),
                width: SinOsc.kr(LFNoise0.kr(1)).range(0.4,0.6),
                mul: amp/2,
            ) +
            VarSaw.ar(
                freq: Lag.kr(4*hz * SinOsc.kr(LFNoise0.kr(1)).range(0.99,1.01),1),
                width: SinOsc.kr(LFNoise0.kr(1)).range(0.4,0.6),
                mul: amp/4,
            )
        );

        // spread the signal
        sig = Splay.ar(sig);

        // pan
        sig = Balance2.ar(sig[0] ,sig[1],SinOsc.kr(
            LFNoise0.kr(0.1).range(0.05,0.5)
        )*0.1);

        // feedback directly taken from the docs
        // https://depts.washington.edu/dxscdoc/Help/Classes/LocalOut.html
        ampcheck = Amplitude.kr(sig);
        sig = sig * (ampcheck &gt; 0.02); // noise gate
        local = LocalIn.ar(2);
        local = OnePole.ar(local, 0.4);
        local = OnePole.ar(local, -0.08);
        local = Rotate2.ar(local[0], local[1],0.2);
        local = DelayN.ar(local, 0.3,
            VarLag.kr(LFNoise0.kr(0.1).range(0.15,0.3),1/0.1,warp:\sine)
        );
        local = LeakDC.ar(local);
        sig = ((local + sig) * 1.25).softclip;

        // filter with drunken walk modulation
        sig = LPF.ar(sig,
            VarLag.kr(LFNoise0.kr(0.3).range(ArrayMin.kr([hz,80]),16000),1/0.3,warp:\sine)
        );

        // feedback
        LocalOut.ar(sig*
            VarLag.kr(LFNoise0.kr(2).range(0.9,1.2),1/2,warp:\sine)
        );

        // panning
        sig = Balance2.ar(sig[0] * 0.2,sig[1]*0.2,SinOsc.kr(
            LFNoise0.kr(0.1).range(0.05,0.2)
        )*0.1)*amp;

        // send it out
        Out.ar(0,sig);
    }
).play;
)
</code></pre><p>Another trick when adding something is to use your mouse to play with parameters. Simply change one of the parameters to <code>MouseX.kr</code> or <code>MouseY.kr</code> and moving your mouse will change that parameter.</p>
<p>From here you can experiment with replacing bits and pieces with different oscillators/filters/effects, or replacing your <code>Mouse</code> parts with different sorts of modulations. for instance, you can replace the filter with a <code>MoogLadder</code> with random modulation in the filter and resonance:</p>
<pre tabindex="0"><code class="language-supercollider" data-lang="supercollider">// add filter
sig = MoogLadder.ar(sig,
        VarLag.kr(LFNoise0.kr(5).range(80,2000),1/5,warp:\sine),
        VarLag.kr(LFNoise0.kr(5).range(0,1.0),1/5,warp:\sine)
);
</code></pre><p>There are endless variations and permutations. Drone on!</p>
<script src="/js/wavesurfer.js"></script>
<script src="/js/waveform.js"></script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://en.wikipedia.org/wiki/Sunn_O" target="_blank" >https://en.wikipedia.org/wiki/Sunn_O</a>))))&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>A sample sequencer for norns</title>
	    <image>https://schollz.com/img/kolor.jpg</image>
      <link>/tinker/kolor/</link>
      <pubDate>Fri, 15 Jan 2021 08:00:46 -0700</pubDate>
      
      <guid>/tinker/kolor/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/510535132" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>this script is born out an exploration of sampler sequencers. i drew inspiration from the op-z and model:samples (though i don&rsquo;t own these i was inspired by how i think they are supposed to work). i had five goals making this sample sequencer:</p>
<ol>
<li>kolor will work as a standalone script <em>and</em> import as a library into most non-grid norns script, meaning <strong>you can use kolor from within another script (if you have a grid)</strong>. i&rsquo;ve made <a href="https://github.com/schollz/oooooo" target="_blank" >a</a> <a href="https://github.com/schollz/downtown" target="_blank" >lot</a> <a href="https://github.com/schollz/barcode" target="_blank" >of</a> <a href="https://github.com/schollz/glitchlets" target="_blank" >scripts</a> and i want to plug in a grid and use it immediately as a &ldquo;groove box&rdquo; in addition to the original host script. to meet this goal, the norns screen actually doesn&rsquo;t provide any information for kolor (except for providing the save/load screen in the parameters). currently <a href="https://github.com/schollz/oooooo/tree/kolor" target="_blank" >you can get this branch of <em>oooooo</em></a> to use with kolor. i plan to add to other scripts as well</li>
<li><strong>step-specific parameter locks with lots of <em>mods</em></strong> - volume, pitch, filters, sample positions, probability, etc. each <em>mod</em> has its own lfo. and an lfo for the lfo&rsquo;s, because why not.</li>
<li><strong>easy pattern chaining and probabilistic chaining</strong> for automatically adding variation (markov chaining).</li>
<li><strong>as little menu-diving as possible</strong>, with very few &ldquo;hold&rdquo; buttons or &ldquo;mode&rdquo; screens. (have to do my best given 128 white lights&hellip;)</li>
<li><strong>stereo samples</strong>! because uncorrelated noise in both ears sounds awesome.</li>
</ol>
<p>at the end i had to make compromises to reach these goals. the compromises mean that there is a list of things that <em>kolor</em> does not do (&hellip;yet, but maybe not at all):</p>
<ol>
<li>all the steps in one track must have the same sound. (this is not a technical limitation, but a menu-diving limitation as it would be hard to determine which step has which sound).</li>
<li>mod parameters are limited to 4-bit resolution, because there are only 15 keys devoted to the selection scale. however, <em>if you use a lfo</em> the values will oscillate with supercollider&rsquo;s bit-depth (32-bit resolution i believe). there may be ways of getting around the 4-bit with some menu diving or more button pushing.</li>
</ol>
<h2 id="requirements">requirements</h2>
<ul>
<li>norns</li>
<li>grid (optional, but recommended). <a href="https://schollz.github.io/kolor/#25" target="_blank" >64-grid is supported</a></li>
</ul>
<h2 id="documentation">documentation</h2>
<p>please see <a href="https://schollz.github.io/kolor/" target="_blank" >schollz.github.io/kolor</a>.</p>
<h2 id="download">download</h2>
<p>get from maiden or from the latest norns image just do</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="p">;</span>install https://github.com/schollz/kolor
</span></span></code></pre></div><p><a href="https://github.com/schollz/kolor" target="_blank" >https://github.com/schollz/kolor</a></p>
]]></description>
    </item>
    
    <item>
	    <title>whowillmatch.com</title>
	    <image>https://schollz.com/img/pink.gif</image>
      <link>/tinker/charities/</link>
      <pubDate>Sat, 28 Nov 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/charities/</guid>
      <description><![CDATA[<p>TLDR: the website is here: <a href="https://whowillmatchmydonation.com/" target="_blank" >whowillmatchmydonation.com</a></p>
<p>Your dollars are precious. And when you make a donation, we bet you want your donation to go the farthest, leave the biggest impact, and make the most difference.</p>
<p>The best way to do that, whether you’ve got $5 to give or $500, is to make your donation to an organization when it is hosting a matching campaign.</p>
<p>The challenge is, these campaigns are fleeting, short-lived, and hard to find out about. They’re not advertised on organizations’ websites and only get announced on social media and via email blasts. Plus, even if you are totally engaged with your fave nonprofits, chances are you’ll never learn about matching campaigns from other, new nonprofits that support causes you’re passionate about.</p>
<p>So, if you’re anything like us — yeah, we admit that we have few dollars to donate AND don’t follow even our fave nonprofits on social AND almost always click unsubscribe when we get too many emails— it is hard to maximize our donations because we never know when a nonprofit has a matching campaign.</p>
<p>That’s why we built <a href="https://whowillmatchmydonation.com/" target="_blank" >whowillmatchmydonation.com</a>. We think you’ll like it if you:</p>
<ul>
<li>Are in the market for making a donation to a nonprofit organization.</li>
<li>Want to do more with less and double your impact.</li>
<li>Are cool with discovering and supporting new nonprofits.</li>
</ul>
<h2>What is a donation match?</h2>
<p>These are kind of like that memory game you played as a child, but instead of having to find a match or remember where one is on your own, the nonprofit you’re donating to already has it!</p>
<p>A matching campaign is when a nonprofit receives a very large pledge from an individual, group, or foundation on the condition that the nonprofit raises their own funds. The individual, group, or foundation, will then match, dollar-for-dollar, the total amount of smaller donations that the organization solicited. These matching campaigns usually have a limited timeframe and can last anywhere from 24 hours to 4 weeks in length.</p>
<h2>How do you know if an organization is running a matching campaign?</h2>
<p>That’s the tricky part. It’s hard to know when nonprofits and organizations are running a matching campaign. They’re NOT typically announced on websites, and usually are shared on social media or via email blasts. This makes it hard to find, especially if you don’t follow all your fave nonprofits on Instagram or Twitter, or if you’re like us and you hate getting all those fundraising emails. </p>
<p>Right now, your best bet is to check whowillmatchmydonation.com and see which organizations are currently tweeting about any current matching campaigns. </p>
<h2>Who does the matching?</h2>
<p>Good question because it sure isn’t us. It’s the people with the big bucks. What we really mean is that matching pledges can come from an individual, a group, or a foundation. Sometimes they come from a nonprofit’s Board of Directors, and commonly come from an outside foundation that wants to help a nonprofit leverage their donors to give give more. </p>
<h2>How much of my gift will be matched?</h2>
<p>All matching campaigns have a predetermined match ratio that is always announced along with the matching campaign. Usually this ratio is 1:1, meaning for every dollar you give, the larger donor will match your gift dollar for dollar. $10 becomes $20, and $100 becomes $200. </p>
<p>However, sometimes the match ratio is less than or greater than 1, in which case you will want to channel your inner middle school math teacher because the match gets slightly more complicated.</p>
<p>Less common, the matching pledge may be 50%. In this case, for every dollar you donate, the larger donor will give an additional 50 cents, so your $10 gift turns into $15). </p>
<p>An even better deal is when you can find a matching campaign at 200%. This means the pledging donor will donate TWICE as much as you do, so your $10 donation becomes $30, and your $100 turns into $300. </p>
<h2>How long do matching campaigns last?</h2>
<p>Sometimes they’re 24 hours long, and sometimes they’re a whole season. There is no set time and it depends on the agreement made between the pledging donor and the nonprofit. Most matching campaigns will almost always include the matching ratio AND the allotted time frame, so check the tweet for more details, or email the nonprofit directly.</p>
<p>A word to the wise: matching campaigns don’t last long and you may miss your window to double your impact. So, if you see an organization listed on <a href="https://whowillmatchmydonation.com/">whowillmatchmydonation.com</a>, don’t wait to make your donation because you may miss that ever-elusive match window.</p>
<h2>Is a matching campaign the same as an employee/corporate matching gift program?</h2>
<p>Not quite, but close. Corporate matching programs allow employees to donate to a nonprofit of their choice, and the employer will then donate money to the same nonprofit at a determined match ratio (usually 1:1). Those programs are only for employees of a given company and are not available to the general public.</p>
<p>Further, don’t confuse any of this matching with employer matching programs that have to do with employees’ 401k and plans and retirement options. </p>
<p>Check with your HR department to see if your workplace offers a corporate matching program.</p>
<p>What we on <a href="https://whowillmatchmydonation.com/">whowillmatchmydonation.com</a> are interested in are the matching gifts that large donors/groups/foundations pledge directly to nonprofits, that get gifted only once that nonprofit raises a specified quantity of funds. The goal of <a href="https://whowillmatchmydonation.com/">whowillmatchmydonation.com</a> is to help you double your impact by finding nonprofits that are running matching campaigns so that those with the big bucks can double your donation. </p>
]]></description>
    </item>
    
    <item>
	    <title>Modulating sample buffers on norns</title>
	    <image>https://schollz.com/img/0e77b2a12f090c6dbefbc0ffcda36aae1c198177.gif</image>
      <link>/tinker/barcode/</link>
      <pubDate>Fri, 20 Nov 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/barcode/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/590413138" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>loop a sound into six voices playing at different levels &amp; pans &amp; rates &amp; positions, modulated by lfos on every parameter.</p>
<p>inspired by <a href="https://llllllll.co/t/cranes/21207" target="_blank" >cranes</a> and the <a href="https://github.com/monome/softcut-studies#3-cut-and-poll" target="_blank" >cut and poll softcut study</a>, i tried to take these ideas to the extreme. in <em>barcode</em> i wanted to utilize all six voices after recording into a buffer.</p>
<h3 id="requirements">Requirements</h3>
<ul>
<li>norns</li>
<li>audio input</li>
</ul>
<h3 id="documentation">Documentation</h3>
<ul>
<li>hold K1 to shift</li>
<li>K2 to pauses LFOs</li>
<li>K3 starts recording</li>
<li>any key stops recording</li>
<li>shift+K2 switches buffer</li>
<li>shift+K3 clears</li>
<li>E1 changes output/rec levels</li>
<li>E2 dials through parameters</li>
<li>E3 adjusts current parameter</li>
<li>shift+E3 adjusts freq of lfo</li>
</ul>
<p>after recording finishes, the corresponding buffer will be played on six different voices.</p>
<p>each voice has six parameters: level, pan, rate, reverse, start point, and end point. each of these parameters is modulated by a randomly initialized lfo (that&rsquo;s 36 lfos!). at this point, the lfos cannot be modulated except by changing the code.</p>
<p>in the ui, the parameters of the voices are represented as six groups of five lines. each group of lines corresponds to one voice. the order of the five lines corresponds to the parameters:</p>
<ol>
<li>level ( L )</li>
<li>pan ( P )</li>
<li>rate ( R )</li>
<li>direction ( D )</li>
<li>tape start/end points ( T )</li>
</ol>
<p>you can bias the modulation for any parameter using E2 to move the corresponding line (a parameter for a voice) and then adjusting with E3. shift+E3 adjusts the frequency of the lfo for that parameter.</p>
<p>the line at the very top is for the overall level, which can be adjusted with E1. during recording, E1 adjusts the recording level.</p>
<p><em>note</em>: the buffer max is 60 seconds. the loop end point is whatever end point you stop recording though.</p>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/barcode
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>norns.online</title>
	    <image>https://schollz.com/img/nornsonline.png</image>
      <link>/tinker/norns.online/</link>
      <pubDate>Fri, 20 Nov 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/norns.online/</guid>
      <description><![CDATA[<p><strong>connect.</strong> <em>norns.online</em> is a norns script, a script library, and a website.</p>
<p><strong>share tapes.</strong> use the <em>norns.online</em> script to upload or download tapes to <a href="https://norns.online/share" target="_blank" >norns.online/share</a>.</p>
<p><strong>share script saves.</strong> individual scripts also can upload/download saves to <a href="https://norns.online/share" target="_blank" >norns.online/share</a> by going to the <code>SHARE</code> parameter from within a particular script (devs: feel free to ask me for help to add it your script).</p>
<p><strong>listen and visualize.</strong> run the <em>norns.online</em> script to beam your audio+visual data to <code>norns.online/&lt;yourname&gt;</code>.</p>
<p><strong>remote control.</strong> the website <code>norns.online/&lt;yourname&gt;</code> has the same inputs as your norns to provide remote access.</p>
<p><strong>collaborate.</strong> use the <em>norns.online</em> script to connect to other norns via &ldquo;<em>rooms</em>.&rdquo; anyone in a &ldquo;room&rdquo; shares live audio. the latency is ~4 seconds, so you can imagine that you all are playing together while being socially distanced by a distance of a quarter-mile.</p>
<h2 id="demo">Demo</h2>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/484176216" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<h2 id="how-nornsonline-works">How norns.online works</h2>
<p>norns runs a service that sends screenshot updates to <code>norns.online/&lt;yourname&gt;</code>. the website at <code>norns.online/&lt;yourname&gt;</code> sends inputs back to norns. norns listens to to inputs and runs the acceptable ones (adjustable with parameters). if enabled, norns will also stream packets of audio and send those to the website. the website will buffer them and play them so anyone with your address can hear your norns.</p>
<p>a pre-compiled <a href="https://github.com/kmatheussen/jack_capture"><code>jack_capture</code></a> periodically captures the norns output into 2-second flac files into a <code>/dev/shm</code> temp directory. each new flac packet is immediately sent out via websockets and then deleted. because of buffering, expect a lag of at least 4 seconds. when in a room, audio from other norns is piped into your norns via <code>mpv</code>. the incoming audio from other norns is added at the very end of the signal chain so (currently) it cannot be used as input to norns engines.</p>
<p>for <em>norns.online</em>,if you are online, you have <a href="https://en.wikipedia.org/wiki/Security_through_obscurity">security through obscurity</a> (weak security). that means that <em>anyone</em> with the url <code>norns.online/&lt;yourname&gt;</code> can access your norns so you can make <code>&lt;yourname&gt;</code> complicated to be more secure. code injection is not possible, as i took precautions to make sure the inputs are sanitized on the norns so that only <code>enc()</code> and <code>key()</code> and <code>_menu.setmode()</code> functions are available. but, even with these functions someone could reset your norns / make some havoc. if this concerns you, don't share <code>&lt;yourname&gt;</code> with anyone or avoid using this script entirely.</p>
<p>
everything you load to <em>norns.online/share</em> is made public but everything is also <strong>authenticated</strong>. authentication means that the data you download from someone named "bob" is truly data from the user who registered as "bob" and not someone posing as "bob". the server does not ensure that "bob" is a good or bad person, but only that the "bob" the server knows is the "bob" that registered with the server. authentication is provided through using rsa key-pairs. the server verifies your data comes from who you say you are by checking the signature on the hash of anything you upload. in theory, other people can obtain your key-pair directly from you to independently verify your data is actually coming from you (so the server need not be trusted), but this is not implemented yet.
</p>
<p>if audio is enabled, a fair amount of bandwidth is used. the norns sends out screenshots periodically, but at the highest fps this is only ~18 kB/s.  however, if audio is enabled - the norns sends flac packets periodically (~170 kB/s = ~616 MB/hr). if you are audio-sharing a room you will be receiving about that much for each norns in the room. i tried reducing bandwidth by using lossy audio (ogg) however the gapless audio playback only worked without pops when using flac or wav.</p>
<p>on a raspberry pi 3b+ this uses about ~4% total CPU for capturing and sending audio data. screenshots also take cpu and higher fps takes more. the exact fps depends on the max fps (set in params) and how fast the screen changes (only updated screens are sent). at max it might take up to 30% of the cpu (15 fps!), but usually its 1-15%.</p>
<p>installation requires <code>ffmpeg</code> and <code>mpv</code> which take up about 300MB. other than that, it only takes up space when you download shared script saves and tapes.</p>
<h2 id="documentation">Documentation</h2>
<ul>
<li>move with any encoder, select with K3</li>
<li>update with K1+K2</li>
</ul>
<p><em>note:</em> if this is the first time running, wait for the <code>mpv</code> and <code>ffmpeg</code> programs to be installed (~300 MB).</p>
<h3 id="beam-your-norns">beam your norns</h3>
<ul>
<li>press <code>go online</code>.</li>
<li>open browser to <code>norns.online/&lt;yourname&gt;</code>.</li>
<li>use norns normally, your norns will stay online in the background.</li>
</ul>
<h3 id="nornsnorns-audio-sharing">norns↔norns audio sharing</h3>
<ul>
<li>go to gloal parameters and make sure both &ldquo;<code>send audio</code>&rdquo; and &ldquo;<code>allow rooms</code>&rdquo; are set to &ldquo;<code>enabled</code>&rdquo;.</li>
<li>change the &ldquo;<code>room</code>&rdquo; to the room you want to share audio. make sure your norns partner uses the same room.</li>
<li>go to main screen and press <code>go online</code>. you should now be sharing audio with any other norns in that room.</li>
<li>adjust &ldquo;<code>room vol</code>&rdquo; to change the level of incoming audio.</li>
</ul>
<h3 id="uses">uses</h3>
<ul>
<li>make an internet radio from your norns</li>
<li>collaborate between two norns</li>
<li>twitch plays norns</li>
<li>make demos (screen capture <code>norns.online</code>)</li>
<li>download screenshots (right-click image at <code>norns.online</code> to download)</li>
<li>tech support other people&rsquo;s norns</li>
<li>sharing tapes / script saves</li>
<li>!?!?!?</li>
</ul>
]]></description>
    </item>
    
    <item>
	    <title>Sequencing samples on norns</title>
	    <image>https://schollz.com/img/abacus.gif</image>
      <link>/tinker/abacus/</link>
      <pubDate>Fri, 20 Nov 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/abacus/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/474676681" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>this norns script creates sequences of samples from a tape. you can load any tape and splice it into up to 26 samples (named a-z). samples can then be patterned into 16-subdivided measures. patterns can then be chained together.</p>
<p>this script was a hard one to make because at a certain point i kept getting caught up playing with for hours instead of making it &ldquo;user friendly.&rdquo; let me know if you find things to improve!</p>
<p>this script builds off others. it is inspired a lot from ideas in <a href="https://llllllll.co/t/glitchlets" target="_blank" >glitchlets</a> (no realtime here) and a lot of code ideas from @mattbiddulph&rsquo;s exquisite <a href="https://llllllll.co/t/beets-1-0/30069" target="_blank" >beets</a> (initially i forked beets but i didn&rsquo;t want to ruin the code with my hacks). also inspiration from the po-33. and, it is inspired by @csboling&rsquo;s beautiful waveform renderings in the latest update.</p>
<p>future directions:</p>
<ul>
<li>fix all the 🐛🐛🐛</li>
<li>add individual parameters for samples</li>
<li>add play trigger</li>
<li>?? requests</li>
</ul>
<h3 id="requirements">Requirements</h3>
<ul>
<li>norns (version 201023+)</li>
</ul>
<h3 id="documentation">Documentation</h3>
<p>any mode</p>
<ul>
<li>K1+E1 changes mode</li>
</ul>
<p>sample mode</p>
<ul>
<li>E1 changes sample</li>
<li>E2/E3 change splice position</li>
<li>K1+K3 starts/stops chain</li>
<li>K2 zooms</li>
<li>K3 plays sample</li>
</ul>
<p>pattern mode</p>
<ul>
<li>E1 changes pattern</li>
<li>E2 selects sample</li>
<li>E3 positions sample</li>
<li>K2 patterns</li>
<li>K3 plays sample</li>
<li>K1+K2 erases position</li>
<li>K1+K3 plays pattern</li>
</ul>
<p>chain mode</p>
<ul>
<li>E2 positions</li>
<li>E3 selects pattern</li>
<li>K2/K3 does effects</li>
</ul>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/abacus
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Raspberry Pi as MIDI to CV device.</title>
	    <image>https://schollz.com/img/monotron/main.jpg</image>
      <link>/tinker/pi-monotron/</link>
      <pubDate>Tue, 08 Sep 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/pi-monotron/</guid>
      <description><![CDATA[<p>The Korg Monotron is a good sounding synthesizer for a relatively cheap price (~$40). One drawback of the Korg Monotron is that it uses a <a href="https://www.sweetwater.com/insync/ribbon-controller/" target="_blank" >ribbon controller</a> to determine the pitch so it is very hard to control the pitch accurately, and almost impossible to be in tune by yourself.</p>
<p>Luckily, Korg has developed this little synth with hacking in mind. These instructions will show you how to use a Raspberry Pi as a cheap MIDI-to-CV controller so you can use the Korg Monotron just like any other MIDI instrument.</p>
<p>This is the first DIY MIDI-to-CV controller that allows you to <strong>automatically tune the Monotron</strong> voltage-to-frequencies <em>and</em> <strong>only has three components</strong> (and no PCBs!). Other solutions - like the <a href="http://beatnic.jp/manuals/monotron-midi/midi-kit.html" target="_blank" >MIDI-IF kit</a> or <a href="https://github.com/elkayem/midi2cv" target="_blank" >Arduino-based midi2cv</a> - require extensive soldering, dozens of components, and require manual tuning.</p>
<h2 id="overview">Overview</h2>
<p>Adding MIDI to the Korg Monotron basically requires converting MIDI input to voltage. The following introductions will work on several CV synthesizers (I think) and are not necessarily specific to the Monotron. Here we are using a cheap DAC (the MCP4725) to send voltages from the Raspberry Pi. The Raspberry Pi uses a Python script <a href="https://github.com/schollz/midi2cv/" target="_blank" >midi2cv.py</a> which listens for MIDI and controls the DAC.</p>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/TuBCexYkAv0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>The magic trick here is that I added a tuning function into the Raspberry Pi (in <a href="https://github.com/schollz/midi2cv/" target="_blank" >midi2cv.py</a>) so you can automatically determine the relationship between voltage and frequency to tune any CV synth. It works by connecting the audio output of your synth to the Raspberry Pi (via USB audio connector) and then computing the FFT to find the fundamental frequency of tones controlled by various voltages.</p>
<p><a href="https://www.instagram.com/p/CE5Xbw4hZBi/" target="_blank" ><img src="/img/monotron/example1.png" alt="Click for demo of the automatic tuning and MIDI keyboard."></a></p>
<p>Below is a step-by-step instruction to take a Korg Monotron and a Raspberry Pi and make it MIDI capable. There are only two solder points and no PCBs and no breadboards!</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li><a href="https://www.amazon.com/gp/product/B07BC7BMHY/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07BC7BMHY&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Raspberry Pi</a> (~$15 on ebay)</li>
<li><a href="https://www.amazon.com/gp/product/B01N905VOY/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B01N905VOY&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >USB audio adapter</a> (~$16, but ~$5 if you by used)</li>
<li><a href="https://www.amazon.com/gp/product/B00684KFAM/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B00684KFAM&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Korg Monotron</a> (~$55)</li>
<li><a href="https://www.amazon.com/gp/product/B00SK8MBXI/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B00SK8MBXI&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >MCP4725</a> (~$5)</li>
<li><a href="https://www.amazon.com/gp/product/B01L5ULRUA/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B01L5ULRUA&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Female-to-female jumper cables</a> (~$6)</li>
<li><a href="https://www.amazon.com/gp/product/B087767KNW/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B087767KNW&amp;linkCode=as2&amp;tag=scholl-20" target="_blank" >Soldering iron</a> (~$17)</li>
<li>MIDI keyboard (optional)</li>
</ul>
<h2 id="part-1-hacking-the-monotron">Part 1: Hacking the Monotron</h2>
<p>We need to first be able to send voltages to the Korg Monotron. To do this we need to connect a wire with the hot voltage and a wire with the ground to the Monotron using soldering.</p>
<h3 id="solder-two-wires-to-the-monotron-pcb">Solder two wires to the Monotron PCB</h3>
<p>Simply unscrew the back of the Monotron and pull out the back. There are four screws, two of them are next to the batteries.</p>
<p><img src="/img/monotron/screws.jpg" alt="Location of the four screws."></p>
<p>Take the Monotron apart, carefully, and you will see the back of the PCB has some gold pads with labels! Korg designed this PCB for us to hack :). Solder one wire to the <code>gate</code> pad and solder another wire to the <code>GND</code> pad (both wires should be female on the opposite end).</p>
<p><img src="/img/monotron/pcb.png" alt="This is the back of the Monotron PCB before you solder."></p>
<p><img src="/img/monotron/soldering.jpg" alt="The only two soldering connections you need to make."></p>
<p>I realize we are not using <code>pitch</code>, but for some reason <code>gate</code> also can be voltage-controlled and acts as a gate (if voltage &gt; 0) and pitch. If you want to watch someone solder, check out <a href="https://www.youtube.com/watch?v=iT__-bH8Q0o" target="_blank" >this great Youtube video for how to solder to the Korg Monotron</a>.</p>
<p><img src="/img/monotron/closed.jpg" alt="Wires sticking out of the hacked Monotron"></p>
<p>Now just close it up, and move the wires so they stick out under the volume control. You can screw everything back together, just make sure not to go too tight.</p>
<h3 id="hook-up-wires-to-the-raspberry-pi">Hook up wires to the Raspberry Pi</h3>
<p>First let&rsquo;s wire up the the MCP4725 to the Raspberry Pi. Attach MCP4725 <code>SDA</code>, <code>SCL</code>, <code>GND</code>, <code>VDD</code> to the Raspberry Pi&rsquo;s <code>GPIO 2 (SDA)</code>, <code>GPIO 3 (SCL)</code>, <code>Ground</code>, and  <code>5V power</code>, respectively. Check the Raspberry Pi <a href="https://www.raspberrypi.org/documentation/usage/gpio/" target="_blank" >pin-out schematic</a> for more details.</p>
<p><img src="/img/monotron/connections_pi.jpg" alt="Wiring between the MCP4725 and the Raspberry Pi"></p>
<p>Now attach the Monotron &ldquo;<code>gate</code>&rdquo; wire to the &ldquo;<code>VOU</code>&rdquo; of the MCP4725. Attach the Monotron &ldquo;<code>GND</code>&rdquo; wire to any ground pin of a Raspberry Pi (<a href="https://www.raspberrypi.org/documentation/usage/gpio/" target="_blank" >schematic</a>).</p>
<p><img src="/img/monotron/connections_all2.jpg" alt="Wiring the Monotron up to the Raspberry Pi with MCP4725."></p>
<p>That&rsquo;s it! No extra breadboard or PCBs necessary!</p>
<h2 id="part-2-tuning-the-monotron">Part 2: Tuning the Monotron</h2>
<p>The Monotron uses a voltage controlled oscillator (VCO).The Monotron voltages can be modified by the <code>INT</code> and the trim pot and these will affect the frequencies for a given voltage. Every time these are altered (or anytime it seems out of tune), you need to tune it. That is, you need to determine which voltage will produce which frequency.</p>
<p>Luckily, instead of tuning each note manually, we can use the Raspberry Pi to self-calibrate it for us!</p>
<h3 id="installing-pre-requisites-on-raspberry-pi">Installing pre-requisites on Raspberry Pi</h3>
<p>Use <a href="https://www.raspberrypi.org/documentation/remote-access/ssh/unix.md" target="_blank" >SSH to get into your Raspberry Pi</a> and install the following prerequisites:</p>
<pre tabindex="0"><code>&gt; sudo apt update
&gt; sudo apt install python3 python3-pip python3-numpy portaudio19-dev sox gnuplot ffmpeg
&gt; sudo -H python3 -m pip install loguru click mido python-rtmidi adafruit-circuitpython-mcp4725 termplotlib aubio
</code></pre><p>Now download the <code>midi2cv.py</code> script:</p>
<pre tabindex="0"><code>&gt; wget https://raw.githubusercontent.com/schollz/midi2cv/master/midi2cv.py
</code></pre><p>Great, now we are ready to tune the synth.</p>
<h3 id="gettin-in-tune">Gettin&rsquo; in tune</h3>
<p>Connect the USB audio to the Raspberry Pi. Then connect the headphone out of the Monotron into the recording line-in with a stereo 1/8&quot; cable.</p>
<p><img src="/img/monotron/tuning.jpg" alt="Connecting the Monotron to the Raspberry Pi for self-calibration."></p>
<p><strong>Important:</strong> Make sure to change the Monotron settings so it is outputting pure tones. Use the following settings for each knob (0 means all the left, 100 means all the way right):</p>
<ul>
<li><code>RATE</code>: 0</li>
<li><code>INT</code>: anywhere you want (don&rsquo;t change it after tuning, though)</li>
<li><code>CUTOFF</code>: 100</li>
<li><code>TIME</code>: 0</li>
<li><code>FEEDBACK</code>: 0</li>
</ul>
<p>Now ssh into the Raspberry Pi and run the following Python script:</p>
<pre tabindex="0"><code>&gt; python3 midi2cv.py --tune
</code></pre><p>This will take about 30 seconds as the Raspberry Pi cycles through voltages (1-5V) while recording in resulting audio. The audio is captured using the USB audio adapter. The captured audio is analyzed using a FFT to determine the fundamental frequency. These voltage-frequency pairs will then be plotted and fit with an exponential curve.</p>
<p><img src="/img/monotron/calibration.png" alt="Frequency-voltage fitting after calibration of the Monotron."></p>
<p>The fit from this curve is saved onto the Raspberry Pi so that when you play the Monotron it will be able to convert the MIDI to frequency and then convert that to voltage.</p>
<p>Now you are ready to play!</p>
<h2 id="4-play-the-monotron">4. Play the Monotron!</h2>
<h3 id="using-a-midi-keyboard">Using a MIDI keyboard</h3>
<p>Simply attach a USB MIDI keyboard to the Raspberry Pi and then run:</p>
<pre tabindex="0"><code>&gt; python3 midi2cv.py --play
</code></pre><p>Now the keyboard will automatically trigger the Raspberry Pi to set the voltage of the Monotron, in tune!</p>
<h3 id="using-midi-sequencing">Using MIDI sequencing</h3>
<p>A keyboard is not necessary to play the Monotron. You can use another program I wrote to sequence MIDI instruments from a simple text file: <a href="https://github.com/schollz/miti" target="_blank" >miti</a>.</p>
<p>First make sure you have portmidi, and download the binary for <code>miti</code> (or you can <a href="https://github.com/schollz/miti" target="_blank" >build it yourself</a>):</p>
<pre tabindex="0"><code>&gt; sudo apt install libportmidi-dev
&gt; wget https://github.com/schollz/miti/releases/download/v0.4.1/miti_arm.tar.gz
&gt; tar -xvzf miti_arm.tar.gz
&gt; sudo mv miti /usr/local/bin/
</code></pre><p>Then create a new sequencing file. The sequencing is easy to write and understand and <a href="https://github.com/schollz/miti#documentation" target="_blank" >documented thoroughly</a>. Here we will make a simple three-chord arpeggio.</p>
<p>In a file names <code>song1.miti</code> write:</p>
<pre tabindex="0"><code>pattern a 

instruments midi through
tempo 120

legato 95
F2 A C F A C F A C F A C F A C F
E2 G C E G C E G C E G C E G C E
D2 F A C D F A C D F A C D F A C
D2 F A C D F A C D F A C D F A C
</code></pre><p>The &ldquo;<code>midi through</code>&rdquo; indicates that we will send the output to the MIDI through port. Now, in one terminal run the <code>midi2cv</code> program:</p>
<pre tabindex="0"><code>&gt; python3 midi2cv.py --play
</code></pre><p>And in another terminal run <code>miti</code> to start sequencing:</p>
<pre tabindex="0"><code>&gt; miti --play song1.miti
</code></pre><p>That&rsquo;s it! You should hear some music. You can edit the <code>song1.miti</code> file in real-time to make any changes to the sequencing.</p>
<h2 id="questions">Questions?</h2>
<p>If you find any problems, feel free to let me know. Find me on instagram (<a href="https://instagram.com/infinitedigits" target="_blank" >@infinitedigits</a>) or Twitter (<a href="https://twitter.com/yakczar" target="_blank" >@yakczar</a>). DMs are open.</p>
]]></description>
    </item>
    
    <item>
	    <title>Digital tape loops for norns</title>
	    <image>https://schollz.com/img/oooooo.gif</image>
      <link>/tinker/oooooo/</link>
      <pubDate>Sat, 29 Aug 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/oooooo/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/590419704" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p>I call this script <em>&ldquo;oooooo&rdquo;</em> because it is composed of six loops. They are like digital tape loops - you can level, pan, speed, slow, shorten, lengthen, dub, overdub any loop at any time.</p>
<p>I was inspired to make this after seeing tape loops circulating (pun sorta intended) all over the place. I like the idea of having multiple independent different loops, with different sizes, played on a different tape players with different eccentricities. I don&rsquo;t have any cassette tapes but I have norns so I wrote this script to try to make digital tape loops.</p>
<p>future directions:</p>
<ul>
<li>arc support (need help)</li>
<li>crow support (need help)</li>
<li>fix all the 🐛🐛🐛</li>
</ul>
<h3 id="requirements">Requirements</h3>
<ul>
<li>audio input</li>
<li>norns</li>
<li>grid (optional)</li>
</ul>
<h3 id="documentation">Documentation</h3>
<ul>
<li>E1 selects loops</li>
<li>E2 changes mode/parameter</li>
</ul>
<p>in tape mode:</p>
<ul>
<li>K2 stops</li>
<li>K2 again resets</li>
<li>K3 plays</li>
<li>K1+K2 clears</li>
<li>K1+K2 again resets</li>
<li>K1+K3 primes recording</li>
<li>K1+K3 again records</li>
</ul>
<p>in other modes:</p>
<ul>
<li>K2 or K3 activates or lfos</li>
<li>E3 adjusts parameter</li>
</ul>
<p>all parameters are available via the global menu.</p>
<p><strong>playback/recording:</strong></p>
<ul>
<li>in tape mode, press K2 to stop/goto 0, and press K3 to start playing (once recorded).</li>
<li>in tape mode, press K1+K3 to prime recording. When primed, recording will automatically begin when incoming audio rises above a threshold. The recording threshold can be set by global parameter &ldquo;<code>recording -&gt; rec thresh</code>&rdquo;.</li>
<li>in tape mode, you can force recording by hitting K1+K3 a second time.</li>
<li>recording stops after traversing the whole loop. You can stop it earlier with K2 or K3 (in tape mode) and that will shrink the loop to that point. You can set recording to continue to the next loop by setting the global parameter &ldquo;<code>recording -&gt; rec thru loops</code>&rdquo; to <code>yes</code>.</li>
<li>by default, volume in &ldquo;pinched&rdquo; when starting/stopping recording to avoid pops from discontinuous signals. You can lower/raise the pinching by adjusting the global parameter &ldquo;<code>vol pinch</code>&rdquo;.</li>
<li>to record a loop over and over, infinitely, change <code>recording -&gt; stop rec after</code> to its max value.</li>
</ul>
<p><strong>quick menu:</strong></p>
<ul>
<li>E2 changes mode/parameter on the quick menu</li>
<li>each parameter can be activated by K2 or K3 (activated lfo), and it can modified by E3</li>
<li>you can adjust the rate in continuous or discrete (±25%, ±50%, etc.) by changing the global parameter &ldquo;<code>continuous rate</code>&rdquo;</li>
<li>the &ldquo;<code>warble</code>&rdquo; mode allows you to temporarily pitch up/down the current loop using E3</li>
</ul>
<p><strong>A loop:</strong></p>
<ul>
<li>&ldquo;A&rdquo; loop can control all loops. the tape mode works as before, but affects all loops.</li>
<li>the quick menu differs from loops but is also activated by K2 or K3, and modulated with E3</li>
</ul>
<p><strong>settings:</strong></p>
<ul>
<li>in <code>startup</code> menu you can load loops on startup, play loops on startup, start loops with random lfos and change the length of the starting loops (in beats). <em>note:</em> these settings persist next time you open <code>oooooo</code>!</li>
<li>in <code>recording</code> menu you can change pre/rec levels, recording threshold for primed recordings, the volume pinchoff, whether to record through loops, and how many times to loop over before stopping recording. note:_ these settings persist next time you open <code>oooooo</code>!</li>
<li>in <code>all loops</code> you can pause all lfos, set loop destruction (which slowly degrades loops), ramp volume up/down, randomize loops on reset, change the reset per loop</li>
<li>in <code>loop X</code> menu you can modify all lfos, and several other parameters of each loop.</li>
</ul>
<p><strong>oooooo ideas:</strong></p>
<ul>
<li><em>audibly ambient:</em> record to each loop and then move them around the screen. <a href="https://www.instagram.com/p/CEzI3mqB_0k/" target="_blank" >video example</a></li>
<li><em>lucid looper:</em> instead of overdubbing one loop, record six separate loops of the same size that have their own stereo field. change <code>startup -&gt; start length</code> to <code>16</code> beats and <code>startup -&gt; start lfos random</code> to <code>yes</code>. then change <code>recording -&gt; rec thru loops</code> to <code>yes</code> and make sure <code>recording -&gt; stop rec after</code> is <code>1</code>. then reload <em>oooooo</em>, and record. <a href="https://www.instagram.com/p/CFBjBxGhJXs/" target="_blank" >video example</a></li>
<li><em>dangerous delay:</em> tape delay with six tapes, that shapeshift. in <code>recording</code> menu set <code>pre level</code> and <code>rec level</code> to 0.5. set <code>stop rec after</code> to max. go to <code>A</code> loop. turn E2 to <code>rand lfo</code> and activate with K2. turn E2 to tape and press K1+K3 to record on all loops forever, making a stereo-field delay. (make it crazier by changing <code>all loops -&gt; randomize on reset</code> to <code>yes</code> and <code>all loops -&gt; reset all every</code> to <code>X beats</code>). <a href="https://www.instagram.com/p/CFFHUNmhxIf/" target="_blank" >video example</a></li>
</ul>
<p><strong>grid docs:</strong></p>
<p>(<em>thank you @tyleretters for <a href="https://tyleretters.github.io/GridStation/" target="_blank" >this absolutely amazingly useful grid doc tool</a>!</em>)</p>
<p><img src="/img/loopmode.png" alt="loop mode"></p>
<p><img src="/img/mellotronmode.png" alt="mellotron mode"></p>
<p><img src="/img/volumemode.png" alt="volume mode"></p>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/oooooo
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Quantize time-bending effects on norns</title>
	    <image>https://schollz.com/img/blndr.png</image>
      <link>/tinker/blndr/</link>
      <pubDate>Tue, 04 Aug 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/blndr/</guid>
      <description><![CDATA[
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://player.vimeo.com/video/590414248" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="vimeo video" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

<br>
<p><code>blndr</code> is a quantized delay with optional time bending effects in the stereo field. this would not have been possible without the stellar <a href="https://monome.org/docs/norns/softcut/" target="_blank" >softcut tutorial</a> and inspiration of randomizing speed shifts from <a href="https://llllllll.co/t/bounds/23336" target="_blank" >bounds</a>. also i have a lot of inspiration and aspiration to recreate the drums from outkast&rsquo;s ms. jackson.</p>
<h3 id="documentation">Documentation</h3>
<p>the line-in audio is fed into a delay loop for a duration of one quarter note, so it automatically becomes quantized to the <code>bpm</code> (ENC1). the amount of delay can be dialed in with <code>level</code> (ENC2) and <code>feedback</code> (K1+ENC2).</p>
<p>the delay loop is randomly time shifted based on the probability from the <code>spin</code> parameter (ENC3). the audio from the delay loop is then fed into a second delay loop that is also time shifted and panned randomly.</p>
<p>the K2/K3 are used to quickly speed up/down the bpm to 1/3 intervals to get some cool polyrhythms (good for drums).</p>
<p>the K1+K2 to toggles muting incoming audio.</p>
<p>the K1+K3 will toggle <em>reverse mode</em> which is a delay that outputs notes in reverse.</p>
<h3 id="install">Install</h3>
<pre tabindex="0"><code>;install https://github.com/schollz/abacus
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Every chord</title>
	    <image>https://schollz.com/img/chordprogressions.png</image>
      <link>/tinker/chords/</link>
      <pubDate>Sat, 01 Aug 2020 08:38:09 -0700</pubDate>
      
      <guid>/tinker/chords/</guid>
      <description><![CDATA[<p>TLDR: the website that resulted from this is <a href="https://schollz.github.io/chords" target="_blank" >schollz.github.io/chords</a>.</p>
<p>In 2009, the musical group <a href="https://www.youtube.com/watch?v=5pidokakU4I" target="_blank" >&ldquo;Axis of Awesome&rdquo; demonstrated the ubiquity of the what they call the <em>&ldquo;Four Chord&rdquo;</em> song</a>. They assert the that most popular songs of the last 40 years use a particular and popular progression of four chords: C-G-Am-F.</p>
<p>I wanted to see just how popular this particular progression is, so I asked the question: <strong>Are the four chords mentioned by the &ldquo;Axis of Awesome&rdquo; (C-G-Am-F) the most represented set of four chords?</strong></p>
<p>Not only did I get the answer, but this question ended up resulting in <a href="https://schollz.github.io/chords/" target="_blank" >a new web app to find chord progressions</a>.</p>
<h2 id="gathering-and-analyzing-song-data">Gathering and analyzing song data</h2>
<p>To gather song data to analyze, I first wrote a program that parsed guitar tabs and extracted chords. The data sample included more than one million songs from almost every genre (except for classical), including blues, jazz, pop, rock, country, folks, etc. I divided each song into sections (i.e. Verse, Chorus, Pre-chorus) and collected the names of chords contained in each section. To aid in comparison, I transposed all the chords in each section of each song so that they were in the same key, the key of C.</p>
<p>Once the data was ready, I found that I had compiled over two thousand different four-chord progressions.</p>
<p>To rank these chord progressions I used <a href="https://en.wikipedia.org/wiki/Markov_chain_Monte_Carlo" target="_blank" >Markov chain Monte Carlo</a> to sample the probability distribution of
four-chord progressions. This produced a list from <em>most</em> to <em>least</em> common chord progressions.</p>
<h2 id="most-four-chord-progressions">Most four-chord progressions</h2>
<p>After analyzing the one million plus songs, I had a list of the most common four-chord progressions. Guess which chord progress topped the list at the most probable?</p>
<p class="chord1 f2">C G Am F</p>
<p class="chord2 f3">probability = 7.6%</p>
<p>Yes, Axis of Awesome <em>was</em> right! Their &ldquo;Four Chord&rdquo; song encompasses the most popular progression I found.</p>
<p>Interestingly, the next six most common sets of four-chord progressions are permutations on these specific four chords:</p>
<table class="table100">
<tr>
<td class="td50">
<p class="chord1 f3">Am F C G</p>
<p class="chord2 f4">probability = 5.1%</p>
</td>
<td class="td50">
<p class="chord1 f3">C Am F G</p>
<p class="chord2 f4">probability = 4.4%</p>
</td>
</tr>
<tr>
<td>
<p class="chord1 f3">F C G Am</p>
<p class="chord2 f4">probability = 2.3%</p>
</td>
<td>
<p class="chord1 f3">C F Am G</p>
<p class="chord2 f4">probability = 2.0%</p>
</td>
</tr>
<tr>
<td>
<p class="chord1 f3">F C Am G</p>
<p class="chord2 f4">probability = 1.5%</p>
</td>
<td>
<p class="chord1 f3">F G C Am</p>
<p class="chord2 f4">probability = 1.4%</p>
</td>
</tr>
</table>
<p>After that you start getting chord patterns that include new minor chords like <em>Em</em> and <em>Dm</em>:</p>
<table class="table100">
<tr>
<td class="td50">
<p class="chord1 f3">C Em Am F</p>
<p class="chord2 f4">probability = 1.3%</p>
</td>
<td class="td50">
<p class="chord1 f3">C G Dm F</p>
<p class="chord2 f4">probability = 1.3%</p>
</td>
</tr>
<tr>
<td>
<p class="chord1 f3">Dm F C G</p>
<p class="chord2 f4">probability = 1.3%</p>
</td>
<td>
<p class="chord1 f3">C Am G F</p>
<p class="chord2 f4">probability = 1.1%</p>
</td>
</tr>
<tr>
<td>
<p class="chord1 f3">Am Dm G C</p>
<p class="chord2 f4">probability = 1.1%</p>
</td>
<td>
<p class="chord1 f3">C Em F G</p>
<p class="chord2 f4">probability = 1.1%</p>
</td>
</tr>
</table>
<p>It isn&rsquo;t until about 60 chord progressions later that you get progressions that have any other chords, such as <em>Bb</em>.  For example, these are the most popular two:</p>
<style>
.table100 {
	width:100%;
}
.td50 {
	width:50%;
}
.chord1 {
	text-align: center;
	font-weight: 900;
	margin-bottom: 0;
	padding-bottom: 0;
}
.chord2 {
	text-align: center;
	margin-top: 0;
	padding-top: 0;
	line-height: 1rem;
}

.f1 { font-size: 3rem; }
.f2 { font-size: 2.25rem; }
.f3 { font-size: 1.5rem; }
.f4 { font-size: 1.25rem; }
.f5 { font-size: 1rem; }
.f6 { font-size: .875rem; }
.f7 { font-size: .75rem; } /* Small and hard to read for many people so use with extreme caution */

@media only screen and (min-width: 30em){
  .f-6-ns,
  .f-headline-ns { font-size: 6rem; }
  .f-5-ns,
  .f-subheadline-ns { font-size: 5rem; }
  .f1-ns { font-size: 3rem; }
  .f2-ns { font-size: 2.25rem; }
  .f3-ns { font-size: 1.5rem; }
  .f4-ns { font-size: 1.25rem; }
  .f5-ns { font-size: 1rem; }
  .f6-ns { font-size: .875rem; }
  .f7-ns { font-size: .75rem; }
}

@media only screen and(min-width: 30em) and (max-width: 60em)) {
  .f-6-m,
  .f-headline-m { font-size: 6rem; }
  .f-5-m,
  .f-subheadline-m { font-size: 5rem; }
  .f1-m { font-size: 3rem; }
  .f2-m { font-size: 2.25rem; }
  .f3-m { font-size: 1.5rem; }
  .f4-m { font-size: 1.25rem; }
  .f5-m { font-size: 1rem; }
  .f6-m { font-size: .875rem; }
  .f7-m { font-size: .75rem; }
}

@media only screen and(min-width: 60em)) {
  .f-6-l,
  .f-headline-l {
    font-size: 6rem;
  }
  .f-5-l,
  .f-subheadline-l {
    font-size: 5rem;
  }
  .f1-l { font-size: 3rem; }
  .f2-l { font-size: 2.25rem; }
  .f3-l { font-size: 1.5rem; }
  .f4-l { font-size: 1.25rem; }
  .f5-l { font-size: 1rem; }
  .f6-l { font-size: .875rem; }
  .f7-l { font-size: .75rem; }
}

</style>
<table class="table100">
<tr>
<td class="td50">
<p class="chord1 f3">C G Bb F</p>
<p class="chord2 f4">probability = 0.3%</p>
</td>
<td class="td50">
<p class="chord1 f3">F C Dm Bb</p>
<p class="chord2 ">probability = 0.3%</p>
</td>
</tr>
</table>
<p>Those are just some of the most popular &ndash; there are many, <em>many</em>, more progressions.</p>
<h2 id="exploring-progressions-from-millions-of-songs">Exploring progressions from millions of songs</h2>
<p>It turns out that there is evidence that what Axis of Awesome claimed in 2009 is true: The most common four-chord progression seems to be C-G-Am-F. The other most common progressions are variations on those four chords, with addition of the other minors (Dm, and Em).</p>
<p>What about the other two thousand progressions? In order to explore them I built a chord-finding app based on the analysis of those million songs: <a href="https://schollz.github.io/chords/" target="_blank" >https://schollz.github.io/chords/</a> Use it to help build your own progressions, and think about new music to create. Sometimes I use it when making <a href="https://infinitedigits.bandcamp.com/" target="_blank" >my music I release</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Text-based MIDI sequencer</title>
	    <image>https://schollz.com/img/miti.png</image>
      <link>/tinker/miti/</link>
      <pubDate>Sat, 01 Aug 2020 07:38:09 -0700</pubDate>
      
      <guid>/tinker/miti/</guid>
      <description><![CDATA[<p>Lately, <a href="https://infinitedigits.bandcamp.com/" target="_blank" >I&rsquo;ve been making music</a> using synthesizers.
Typically, I make music by performing a song on multiple synthesizers - in realtime -  rather than recording separate pieces and assembling them in a digital audio workstation software (e.g. Ableton / Garageband).
In order to perform on multiple synthesizers I use &ldquo;sequencing&rdquo; - a feature common to modern synthesizers which provide a way to preset when notes play.</p>
<p>Sequencing a synthesizer is often time consuming (you have to press one note at a time), and often unforgiving (a wrong note often means having to restart), but most importantly: only a single part of each song can be sequenced at a time due to onboard constraints (typically 64-128 notes).</p>
<p>I can avoid limitations of onboard sequencers, and at the same time <em>unify</em> all my instruments around a single sequencer by writing my own program. I decided to try this, by writing a program in Go that enables sequencing of multiple instruments simultaneously, with any number of parts and patterns. I call this software, <a href="https://github.com/schollz/miti" target="_blank" ><em>miti</em></a>, because I think of it as MIDI but with text (<em>miti</em> = <em>musical instrument textual interface</em>).</p>
<h1 id="miti-in-action">miti in action</h1>
<p><a href="https://github.com/schollz/miti" target="_blank" ><em>miti</em></a> is able to sequence any number of synthesizers from any type of computer (including Raspberry Pis) with low latency and low jitter.</p>
<p><a href="https://github.com/schollz/miti" target="_blank" ><em>miti</em></a> also comes with a high-level language to write sequences. Here is a simple song I scripted which contains three parts - a melody, a chord, and an background counter arpeggio - for three instruments and two different patterns chained together. The sequencing language is stripped down and focuses only on notes, rests (the periods &ldquo;.&rdquo;), and the information about which instrument, tempo and pattern.</p>
<pre tabindex="0"><code>tempo 110
chain 1 2 2 2

pattern 1
instruments op-1
legato 20
Bb4  G4 Eb- Eb . . . . Bb4 G4 Eb- Eb . . . .
Bb4 G4 Eb- Eb . . . . Bb4 G4 Eb- Eb . . . .
G4  Eb4 C- C . . . . G4  Eb4 C- C . . . . 
G4  Eb4 C- C . . . . G4  Eb4 C- C . . . . 

pattern 2 
instruments op-1
legato 40
Bb4  G4 Eb- Eb . . . . Bb4 G4 Eb- Eb . . . .
Bb4 G4 Eb- Eb . . . . Bb4 G4 Eb- Eb . . . .
G4  Eb4 C- C . . . . G4  Eb4 C- C . . . . 
G4  Eb4 C- C . . . . G4  Eb4 C- C . . . . 

instruments boutique
legato 90
Eb3GBb 
D3FBb
C3EbG- 
C3EbG

instruments nts-1
legato 50
Eb1 G Bb Eb G Bb  Eb G Bb  Eb G Bb  Eb G Bb  Eb G Bb 
D1 F Bb D F Bb  D F Bb  D F Bb D F Bb D F Bb
C1 Eb G C Eb G C Eb G C Eb G C Eb G C Eb G
C1 Eb G C Eb G C Eb G C Eb G C Eb G C Eb G
</code></pre><p>And here is how it sounds:</p>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/ZFbXcff8u6c?start=4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="padding-bottom: 2em;"></iframe>
<p>If you are interested in using <em>miti</em>, check out <a href="https://github.com/schollz/miti#documentatoin" target="_blank" >the documentation</a>. The rest of this article is about the design and problems inherent with realtime applications and using Go to tackle them.</p>
<h2 id="developing-a-midi-sequencer">Developing a MIDI sequencer</h2>
<p>Developing a MIDI sequencer is somewhat daunting because of the realtime nature of music. A sequence off by a <a href="https://en.wikipedia.org/wiki/Precedence_effect" target="_blank" >mere 40 milliseconds can be detected</a> by most human listeners (in my experience, sometimes even 20 milliseconds can be detected by adept musicians). A program that causes notes to play with errors of tens of milliseconds is not viable, then.</p>
<p>MIDI messages are sent over USB and can require sending messages at transfer rates of 200 messages / second (when controlling three synthesizers emitting 1/8th notes at 240 bpm), and a single burst might involve 10 messages in a few milliseconds. The latency of a program needs to have millisecond latency within it to make sure timing stays accurate.</p>
<h2 id="using-go-for-a-realtime-audio-app">Using Go for a realtime audio app</h2>
<p>I opted to use Go so I could make use of my knowledge of the language, as well as some language features (goroutines), and as well as make use of the already developed <code>portmidi</code> libraries.</p>
<p>A brief overview of my implementation is as follows.</p>
<p>I developed the program using different packages (a typically go pattern) - i.e. a metronome package, a midi package, a music package, a sequencer package - where each package encapsulates a certain domain of the sequencer. This turned out to be quite useful, especially when I decided to test other midi drivers and all I had to do was change an import statement.</p>
<p>The core of my sequencer is the metronome package defines a universal clock (using <code>timer.NewTicker</code>) which executes a callback function on each tick. The tick rate is set to the standard MIDI pulse rate - 24 pulses per quarter note. The sequencer takes the callback and determines whether notes should be emitted and then sends notes to another callback that is executed by the main program&rsquo;s midi driver.</p>
<p>I decided to write the midi driver so that each instrument has its own dedicated thread, which works well if you don&rsquo;t have too many instruments. So upon receiving a note from an instrument, it determines the write go channel and communicates the note. This is also easy to do in Go!</p>
<p>Overall I found that the program was easy to write, and optically, had low latency (as the internal timer and note emission doesn&rsquo;t exceed ~2 milliseconds). I used <code>GODEBUG=gctrace=1</code> and found that there were no garbage collection stop-the-world pauses that could introduce extra latency (a common criticism of using gc languages for realtime applications).</p>
<h2 id="chasing-down-jitter">Chasing down jitter</h2>
<p>The biggest challenge with MIDI sequencing is called <em>jitter</em>. The <em>jitter</em> is amount of variation in time of the actual audio output of a sequence of notes from their originally intended sequence in time. For example, if the MIDI sequencer intends to play three notes at 60 bpm, at seconds 0.0, 1.0 and 2.0; then the jitter might cause the notes to actually play at 0.02, 0.98, and 2.05 seconds.</p>
<p><img src="/img/jitter.png" alt="Jitter is when events in time don&rsquo;t trigger sound in the same time"></p>
<p>Jitter can be devastating if it is greater than 30 milliseconds, because it will be detectable by a human ear. I wanted to determine the jitter coming from this sequencer I wrote.</p>
<p>I set the sequencer to output a constant sequence of notes at a constant speed (1/8th notes at 220 bpm). During the sequence I recorded two separate sections, of about 10-20 seconds. I then aligned these sections and then calculated the millisecond difference between the rise of corresponding notes between the two separate recordings.</p>
<p><img src="https://user-images.githubusercontent.com/6550035/88127425-6a70f580-cb88-11ea-8658-075adcd1c8b6.PNG" alt="Example of jitter between two waveforms"></p>
<p>If there was no jitter, there would be no difference, but as you can see, there is a different sometimes.</p>
<p><img src="/img/jitter2.png" alt="Jitter on the Raspberry pi"></p>
<p>I found that I could reduce the jitter to about 5 milliseconds, with rare spikes at 20 milliseconds by using a buffer in the MIDI calls (a feature built-in to <code>portmidi</code>). This was not bad. And using this quantification I was able to determine that the enemy of jitter can be a cheap synthesizer (a cheap synth I have adds about 20 milliseconds of jitter), and that, typically, the jitter doesn&rsquo;t increase with the number of instruments.</p>
<h2 id="conclusion">Conclusion</h2>
<p>With the C-bindings, I was happy to learn that Go works well as a realtime audio language, when it comes to MIDI at least. I&rsquo;ve started using this program when <a href="https://infinitedigits.bandcamp.com/" target="_blank" >making my own music</a>. It&rsquo;s free and open-source if you want to <a href="https://github.com/schollz/miti" target="_blank" >try it yourself</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>How to install portmidi</title>
	    <image>https://schollz.com/img/portmidi.jpg</image>
      <link>/tinker/portmidi/</link>
      <pubDate>Thu, 16 Jul 2020 19:46:59 -0700</pubDate>
      
      <guid>/tinker/portmidi/</guid>
      <description><![CDATA[<p>The Go libraries for MIDI devices - <a href="https://github.com/rakyll/portmidi" target="_blank" >github.com/rakyll/portmidi</a> and <a href="https://github.com/xlab/portmidi" target="_blank" >github.com/xlab/portmidi</a> - both use the <a href="http://portmedia.sourceforge.net/portmidi/" target="_blank" >portmidi</a> library with CGo. This means that building any package that requires these dependencies will need to also compile <code>portmidi</code>.</p>
<p>Here are simple instructions for macOS, Linux, and Windows to get started with <code>portmidi</code> and Go.</p>
<h2 id="install-portmidi-on-mac">Install portmidi on Mac</h2>
<p>Open a terminal and do</p>
<pre tabindex="0"><code>brew install portmidi
</code></pre><h2 id="install-portmidi-on-linux">Install portmidi on Linux</h2>
<p>Open a terminal and do:</p>
<pre tabindex="0"><code>sudo apt install libportmidi-dev
</code></pre><h2 id="install-portmidi-on-windows">Install portmidi on Windows</h2>
<p>First install <a href="https://www.msys2.org/" target="_blank" >MSYS2</a>. This will allow you to add a natively built PortMidi directly onto your system.</p>
<p>Once that is installed, run <code>MSYS2</code> and in the command prompt install PortMidi with the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl">&gt; pacman -S mingw-w64-x86_64-portmidi
</span></span></code></pre></div><p>In order to get it to work, in Powershell you should add the following two environmental variables when you try to build a Go program (note, if you didn&rsquo;t install MSYS2 into <code>C:\msys64</code> you will need to change that directory):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a></span><span class="cl"><span class="nv">$env:CGO_CFLAGS</span><span class="p">=</span><span class="s2">&#34;-IC:\msys64\mingw64\include&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a></span><span class="cl"><span class="nv">$env:CGO_LDFLAGS</span><span class="p">=</span><span class="s2">&#34;-LC:\msys64\mingw64\lib&#34;</span>
</span></span></code></pre></div><p>Simply copy and paste those environmental variables into the terminal you are using before doing a <code>go build</code> or <code>go run</code>.</p>
<h2 id="test-it-out">Test it out</h2>
<p>To test it, run and build this program:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="ln" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a></span><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a></span><span class="cl">	<span class="s">&#34;github.com/rakyll/portmidi&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a></span><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a></span><span class="cl">	<span class="nx">err</span> <span class="o">:=</span> <span class="nx">portmidi</span><span class="p">.</span><span class="nf">Initialize</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a></span><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a></span><span class="cl">		<span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a></span><span class="cl">	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;[INFO] total MIDI devices:&#34;</span><span class="p">,</span> <span class="nx">portmidi</span><span class="p">.</span><span class="nf">CountDevices</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If everything worked correctly, you should be able to compile and run!</p>
]]></description>
    </item>
    
    <item>
	    <title>Making a mellotron from a cassette player.</title>
	    <image>https://schollz.com/img/s1/synth.jpg</image>
      <link>/tinker/tape-synth/</link>
      <pubDate>Sat, 27 Jun 2020 16:47:18 -0700</pubDate>
      
      <guid>/tinker/tape-synth/</guid>
      <description><![CDATA[<p>I love weird music projects. I&rsquo;ve recorded <a href="https://infinitedigits.bandcamp.com/album/alsep12" target="_blank" >an album soundtracking the Apollo 12 mission</a> and recently <a href="/blog/heartbeat" >used my heart beat to control music tempo</a>. You can find more of my music at <a href="https://music.byinfinitedigits.com" target="_blank" >music.byinfinitedigits.com</a></p>
<p>Recently, I finished converting an old walkman-style cassette player into a MIDI-controlled synthesizer using an Arduino and a little code - dubbed <strong>the cassette synthesizer</strong>.</p>
<p>The <strong>cassette synthesizer</strong> works by playing a pre-recorded drone (a single tone) from tape and then modulating the speed of the playback to pitch up the tone to any pitch you want. I got this idea for the cassette synthesizer from <a href="http://www.ondemagnetique.com/" target="_blank" >Onde Magnétique</a> who got the idea from the <a href="http://www.mellotron.com/" target="_blank" >Mellotron</a>.</p>
<p>Here&rsquo;s a video of me playing the cassette synthesizer, controlled via MIDI by my other keyboard:</p>
<p align="center" style="max-width: 100%;">
<iframe height="315" src="https://www.youtube.com/embed/LdBik_Zlwy0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="width: 100%;"></iframe>
</p>
<p>The neat thing about this synthesizer is that it has a very &ldquo;analog&rdquo; quality to the changing of notes - the pitch often <em>slides</em> between notes in a neat way (i.e. lots of <a href="https://en.wikipedia.org/wiki/Portamento" target="_blank" ><em>portamento</em></a>). It also is versatile because you can record <em>any</em> sound to the tape and use that as your synthesizer.</p>
<h2 id="making-a-cassette-synthesizer">Making a cassette synthesizer</h2>
<p>To make one of these things is actually <em>really</em> easy. I found a great video from Analog Industries showing exactly how to <a href="https://www.youtube.com/watch?v=pF6Yegj7A9o" target="_blank" >hack a cassette player</a> to add voltage control to the cassette player. I followed that and then wrote a simple MIDI controller in the browser to modulate the voltage to specific notes.</p>
<p>These instructions will take you through the hardware (Arduino / Cassette player) and the software for the MIDI controller.</p>
<h3 id="supplies">Supplies</h3>
<ul>
<li>GE 3-5362A tape player ($15, eBay has tons of them)</li>
<li><a href="https://www.amazon.com/gp/product/B008GRTSV6/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B008GRTSV6&amp;linkCode=as2&amp;tag=scholl-20&amp;linkId=7bcd2ae0b8147ff819937b73da545cfb" target="_blank" >Arduino</a> ($23)</li>
<li><a href="https://www.amazon.com/gp/product/B00SK8MBXI?ie=UTF8&amp;tag=scholl-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B00SK8MBXI" target="_blank" >MCP4725 DAC</a> ($11)</li>
<li><a href="https://www.amazon.com/gp/product/B07Y8KR21P?ie=UTF8&amp;tag=scholl-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B07Y8KR21P" target="_blank" >Audio jack breakout</a> ($8)</li>
<li><a href="https://www.amazon.com/gp/product/B07GD2BWPY?ie=UTF8&amp;tag=scholl-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B07GD2BWPY" target="_blank" >Jumper wires</a> ($5, if you don&rsquo;t have)</li>
<li><a href="https://www.amazon.com/gp/product/B07Q2B4ZY9?ie=UTF8&amp;tag=scholl-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B07Q2B4ZY9" target="_blank" >Solder iron + supplies</a> ($25, and it lasts a lifetime)</li>
</ul>
<h2 id="hacking-the-ge-3-5362a-tape-player">Hacking the GE 3-5362A tape player</h2>
<p><em>Note:</em> You don&rsquo;t necessarily have to use the GE 3-5362A. Any tape player with a variable playback will work. You&rsquo;ll just have to figure out how to hook up the voltage :)</p>
<p>First thing is to open up the tape player. There are four screws on the back. Just unscrew them and open it carefully. The power lines are connected on the back plate so just don&rsquo;t rip those out.</p>
<p><img src="/img/s1/overview.jpg" alt="Hacking the GE 3-5362A tape player."></p>
<p>To get this tape player working for us we will solder two new components. First we will solder in the <code>Vin</code> which will allow us to control the speed of the cassette player with a voltage controller. Then we will add an audio splitter as a <code>Line in</code> which will let us record directly onto tape (in case you don&rsquo;t have a tape deck).</p>
<p>If you don&rsquo;t know how to solder - don&rsquo;t sweat. Its easy. Check out <a href="https://youtu.be/HTy9Z9LpA2U?t=1011" target="_blank" >this video</a> which shows you how to use the existing solder pad and put something onto it.</p>
<h3 id="adding-the-vin-to-control-tape-speed">Adding the <code>Vin</code> to control tape speed</h3>
<p>Locate the dial that says &ldquo;Variable Speed Playback&rdquo;. This is where we will splice in two lines. I like to use red for active and brown/green for ground. Attach the active line to <code>VS+</code> and the ground to the pad right below the one labeled <code>B+</code>.</p>
<p><img src="/img/s1/vs.jpg" alt="Solder a cable to VS+ and one to the pad next to B+."></p>
<p>A note - I like to use jumper cables that have a female end so I can easily plug stuff into here!</p>
<h3 id="add-in-a-line-in-to-record-directly-onto-tape">Add in a <code>Line in</code> to record directly onto tape</h3>
<p>Locate the red and black cables plugged into pads labeled &ldquo;MIC-&rdquo; and &ldquo;MIC+&rdquo;. You can solder and remove these cables and attach your own from the audio breakout cable. Just solder red to &ldquo;MIC+&rdquo; and black to &ldquo;MIC-&rdquo;.</p>
<p><img src="/img/s1/linein.jpg" alt="Solder a line-in via the MIC- and MIC+."></p>
<h3 id="record-a-drone-to-tape">Record a drone to tape</h3>
<p>Put in a tape and record via the line in! Record any note you want, usually a single drone on C works well as a starting point. Record a long time - 30 minutes or so (this would be a good place for tape loops if you have them!). Make sure to record a single tone because you will pitch it up/down later.</p>
<p><img src="/img/s1/rec.jpg" alt="Recording a drone from my OP-1 to the tape."></p>
<h2 id="setup-an-arduino-as-midi-interface">Setup an Arduino as MIDI interface</h2>
<p>The Arduino serves as a bridge between MIDI and the voltage. A MIDI keyboard is plugged into a computer and a Chrome browser gets the response. The browser executes a request to a server running which sends the serial information for the voltage change to the Arduino, which then modulates the voltage on the walkman.</p>
<p><img src="/img/s1/arduino.png" alt="Connecting the MCP4725 DAC to the Arduino"></p>
<p>First, simply connect the Arduino to the MCP4725. The MCP4725 is a digital-to-analog converter (DAC) that allows modulating specific voltages directly from the Arduino. The <code>OUT</code> from the MCP4725 should go to the RED wire you connected to the cassette player. Then attach the ground wire on the cassette player to the ground on the Arduino.</p>
<p>The code for the Arduino just communicates via a Serial port to send voltages. For instance, sending <code>voltage1.1</code> will change the output voltage on the DAC to <code>1.1</code>. Here is the code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;Wire.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;Adafruit_MCP4725.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="n">Adafruit_MCP4725</span> <span class="n">dac</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl"><span class="n">String</span> <span class="n">sdata</span> <span class="o">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span> <span class="c1">// Initialised to nothing.
</span></span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">started</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="nf">begin</span><span class="p">(</span><span class="mi">9600</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl">  <span class="c1">// For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
</span></span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl"><span class="c1"></span>  <span class="c1">// For MCP4725A0 the address is 0x60 or 0x61
</span></span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl"><span class="c1"></span>  <span class="c1">// For MCP4725A2 the address is 0x64 or 0x65
</span></span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl"><span class="c1"></span>  <span class="n">dac</span><span class="p">.</span><span class="nf">begin</span><span class="p">(</span><span class="mh">0x62</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">  <span class="nf">pinMode</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl">  <span class="nf">pinMode</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">&#34;Begin&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">started</span> <span class="o">==</span> <span class="nb">false</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a></span><span class="cl">    <span class="n">started</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a></span><span class="cl">    <span class="n">dac</span><span class="p">.</span><span class="nf">setVoltage</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a></span><span class="cl">    <span class="nf">digitalWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a></span><span class="cl">    <span class="nf">digitalWrite</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a></span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-27"><a class="lnlinks" href="#hl-0-27">27</a></span><span class="cl">  <span class="n">byte</span> <span class="n">ch</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-28"><a class="lnlinks" href="#hl-0-28">28</a></span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">Serial</span><span class="p">.</span><span class="nf">available</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-29"><a class="lnlinks" href="#hl-0-29">29</a></span><span class="cl">    <span class="n">ch</span> <span class="o">=</span> <span class="n">Serial</span><span class="p">.</span><span class="nf">read</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-0-30"><a class="lnlinks" href="#hl-0-30">30</a></span><span class="cl">    <span class="n">sdata</span> <span class="o">+=</span> <span class="p">(</span><span class="kt">char</span><span class="p">)</span><span class="n">ch</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-31"><a class="lnlinks" href="#hl-0-31">31</a></span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ch</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-32"><a class="lnlinks" href="#hl-0-32">32</a></span><span class="cl">      <span class="n">sdata</span><span class="p">.</span><span class="nf">trim</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-0-33"><a class="lnlinks" href="#hl-0-33">33</a></span><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">sdata</span><span class="p">.</span><span class="nf">indexOf</span><span class="p">(</span><span class="s">&#34;voltage&#34;</span><span class="p">)</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-34"><a class="lnlinks" href="#hl-0-34">34</a></span><span class="cl">        <span class="n">sdata</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-35"><a class="lnlinks" href="#hl-0-35">35</a></span><span class="cl">        <span class="kt">float</span> <span class="n">newVal</span> <span class="o">=</span> <span class="n">sdata</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-0-36"><a class="lnlinks" href="#hl-0-36">36</a></span><span class="cl">        <span class="c1">// set voltage
</span></span></span><span class="line"><span class="ln" id="hl-0-37"><a class="lnlinks" href="#hl-0-37">37</a></span><span class="cl"><span class="c1"></span>        <span class="kt">float</span> <span class="n">newVoltage</span> <span class="o">=</span> <span class="nf">round</span><span class="p">(</span><span class="mf">910.0</span> <span class="o">*</span> <span class="n">newVal</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-38"><a class="lnlinks" href="#hl-0-38">38</a></span><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">newVoltage</span> <span class="o">&gt;</span> <span class="mi">4095</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-39"><a class="lnlinks" href="#hl-0-39">39</a></span><span class="cl">          <span class="n">newVoltage</span> <span class="o">=</span> <span class="mi">4095</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-40"><a class="lnlinks" href="#hl-0-40">40</a></span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-41"><a class="lnlinks" href="#hl-0-41">41</a></span><span class="cl">        <span class="kt">uint16_t</span> <span class="n">newVolts</span> <span class="o">=</span> <span class="kt">uint16_t</span><span class="p">(</span><span class="n">newVoltage</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-42"><a class="lnlinks" href="#hl-0-42">42</a></span><span class="cl">        <span class="n">dac</span><span class="p">.</span><span class="nf">setVoltage</span><span class="p">(</span><span class="n">newVolts</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-43"><a class="lnlinks" href="#hl-0-43">43</a></span><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">&#34;volts: &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-44"><a class="lnlinks" href="#hl-0-44">44</a></span><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="n">newVolts</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-45"><a class="lnlinks" href="#hl-0-45">45</a></span><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-46"><a class="lnlinks" href="#hl-0-46">46</a></span><span class="cl">        <span class="n">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">&#34;?&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-47"><a class="lnlinks" href="#hl-0-47">47</a></span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-48"><a class="lnlinks" href="#hl-0-48">48</a></span><span class="cl">      <span class="n">sdata</span> <span class="o">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-49"><a class="lnlinks" href="#hl-0-49">49</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-50"><a class="lnlinks" href="#hl-0-50">50</a></span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-51"><a class="lnlinks" href="#hl-0-51">51</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="server-to-send-serial-commands">Server to send serial commands</h3>
<p>To communicate with the Arduino you can use a simple server that hooks the MIDI to the voltage Serial. You can get this code from <a href="https://github.com/schollz/tape-synth" target="_blank" >github.com/schollz/tape-synth</a>. Make sure you have Golang installed (install <a href="https://golang.org/dl/" target="_blank" >Golang</a>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">$ git clone https://github.com/schollz/tape-synth
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl">$ <span class="nb">cd</span> tape-synth
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl">$ go build  
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a></span><span class="cl">$ ./tape-synth -com ARDUINOCOM
</span></span></code></pre></div><p><em>Note:</em> you&rsquo;ll have to run in <code>sudo</code> to access the USB ports usually if you are using Linux. The <code>ARDUINOCOM</code> is the COM-port for the USB-connected Arduino. For Windows it is usually <code>COM4</code>.</p>
<p>The server will serve a webpage from <code>index.html</code>. You should open up Chrome to <code>localhost:8080</code> and you&rsquo;ll be able to connect a MIDI keyboard and send voltages to the Arduino (Chrome is the most popular <a href="https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess" target="_blank" >MIDI-capable browser</a>).</p>
<h2 id="connect-everything-and-play">Connect everything and play!</h2>
<p>To get it going, start the serial server. Plug in a midi keyboard. Open chrome to the <code>localhost:8080</code>. Turn on the cassette player and <strong>start jamming!</strong> A note on the keyboard should translate into a note on the cassette synthesizer.</p>
<h3 id="tuning-the-synth">Tuning the synth</h3>
<p>Because we are dealing with mechanical pitch shifting (via tape speed) its necessary to tune the synth. To tune each pitch, just open <code>index.html</code> and edit the <code>voltageMap</code>. Each note should have a voltage (between 0 and 3) associated with its pitch.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="kd">var</span> <span class="nx">voltageMap</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl">    <span class="s2">&#34;C&#34;</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl">    <span class="s2">&#34;C#&#34;</span><span class="o">:</span> <span class="mf">0.7</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl">    <span class="s2">&#34;D&#34;</span><span class="o">:</span> <span class="mf">0.9</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl">    <span class="s2">&#34;D#&#34;</span><span class="o">:</span> <span class="mf">1.2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl">    <span class="s2">&#34;E&#34;</span><span class="o">:</span> <span class="mf">1.4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl">    <span class="s2">&#34;F&#34;</span><span class="o">:</span> <span class="mf">1.62</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl">    <span class="s2">&#34;F#&#34;</span><span class="o">:</span> <span class="mf">1.85</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">    <span class="s2">&#34;G&#34;</span><span class="o">:</span> <span class="mf">2.25</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl">    <span class="s2">&#34;G#&#34;</span><span class="o">:</span> <span class="mf">2.6</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl">    <span class="s2">&#34;A&#34;</span><span class="o">:</span> <span class="mf">3.0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl">    <span class="s2">&#34;A#&#34;</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a></span><span class="cl">    <span class="s2">&#34;B&#34;</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="thats-it">That&rsquo;s it!</h2>
<p>If something is unclear (it probably is) don&rsquo;t hesitate to reach out to me. I&rsquo;m <a href="https://twitter.com/yakczar" target="_blank" >yakcar @ twitter</a> and <a href="https://instagram.com/infinitedigits" target="_blank" >infinitedigits @ instagram</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Heartbeat metronome.</title>
	    <image>https://schollz.com/img/pulse.jpg</image>
      <link>/tinker/heartbeat/</link>
      <pubDate>Fri, 12 Jun 2020 12:17:31 -0700</pubDate>
      
      <guid>/tinker/heartbeat/</guid>
      <description><![CDATA[<p>Yes, its 2020 and entirely possible to <a href="https://www.youtube.com/watch?v=qSKBtEBRWi4" target="_blank" >control synthesizers with your thoughts</a>, so of course its entirely possible to control your synthesizers with your pulse. In fact, its entirely possible to control your synthesizers with your heart rate with only ~$50 of equipment.</p>
<p><em>&ldquo;Why?&rdquo;</em>, you ask? I don&rsquo;t know. Why not? It would make <a href="https://en.wikipedia.org/wiki/Dada" target="_blank" >dadas</a> proud. Also, who says that a tempo needs to be fixed, lets make things more dynamic. Lets make the music that flows in your blood!</p>
<h2 id="show-me">Show me</h2>
<p>The code is here: <a href="https://github.com/schollz/heartbpm" target="_blank" >https://github.com/schollz/heartbpm</a></p>
<p>Here&rsquo;s a video of me using my heart rate to control three synthesizers.</p>
<center>
<iframe width="560" height="315" src="https://www.youtube.com/embed/o_GRabvE5Pw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</center>
<p>Although you can do this with almost any synthesizer (more on that below), for this I&rsquo;m using the Roland SH01a, and the Teenage Engineering OP-1 and Pocket Operator PO-33. Luckily my pulse rate averaged around 80 BPM which is what I would like for this particular song.</p>
<h3 id="how-does-it-work">How does it work?</h3>
<p>The system is really simple. Here&rsquo;s a little schematic:</p>
<p><img src="/img/schematic1.PNG" alt="A basic schematic of the setup"></p>
<p>Connect a pulse sensor to your finger. The pulse sensor is connected to an Arduino. The Arduino can detect a pulse and send serial data to a computer. The computer runs a server that reads the serial data which updates a web page. The web page uses Web MIDI to send out the MIDI clock to all connected synths!</p>
<p>The following instructions will give you a more detailed instruction.</p>
<h2 id="stuff-to-buy">Stuff to buy.</h2>
<ul>
	<li>
		<a target="_blank" href="https://www.amazon.com/gp/product/B008GRTSV6/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B008GRTSV6&linkCode=as2&tag=scholl-20&linkId=273d395910f0ec3a1c1da85d779214fb">
			Arduino ($23)
		</a>
	</li>
	<li>
		<a target="_blank" href="https://www.amazon.com/gp/product/B01CPP4QM0/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B01CPP4QM0&linkCode=as2&tag=scholl-20&linkId=e3026cfdfee3a180a780748bd1dce2e4">
			Pulse sensor ($25)
		</a>
	</li>
	<li>
		<a target="_blank" href="https://www.amazon.com/gp/product/B06XQYN77L/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B06XQYN77L&linkCode=as2&tag=scholl-20&linkId=ef5f1fd6a1f83b3fbbcabd242e4ff4cc">
			Audio isolator (optional, $9)
		</a>
	</li>
	<li>
		Synthesizer with MIDI (basically *any* synthesizer made in the last 10 years)
	</li>
	<li>Computer</li>
</ul>
<h2 id="get-the-code">Get the code.</h2>
<p>First get the code. You can <a href="https://github.com/schollz/heartbpm" target="_blank" >clone from Github</a> or you can <a href="https://github.com/schollz/heartbpm/archive/master.zip" target="_blank" >download it directly</a>.</p>
<h2 id="setup-the-hardware">Setup the hardware.</h2>
<p>First you can set up the pulse sensor. Simply attach the pulsesensor to the arduino.</p>
<center>
<img alt="Connecting the pulse sensor to the Arduino" src="/img/heartbeat_bb_POoEsoKAle.jpg" width="100" style="max-width: 300px;"/>
</center>
<p>Now use the <a href="https://www.arduino.cc/en/main/software" target="_blank" >Arduino IDE</a> to upload the <code>heartbeat.ino</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="kt">int</span> <span class="n">pulseSensorPurplePin</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>       
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="kt">int</span> <span class="n">signal</span><span class="p">;</span>         
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="kt">int</span> <span class="n">Threshold</span> <span class="o">=</span> <span class="mi">550</span><span class="p">;</span>      
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="kt">int</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="nf">begin</span><span class="p">(</span><span class="mi">9600</span><span class="p">);</span>       
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl">  <span class="n">signal</span> <span class="o">=</span> <span class="nf">analogRead</span><span class="p">(</span><span class="n">pulseSensorPurplePin</span><span class="p">);</span>  
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl">  <span class="n">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="n">signal</span><span class="p">);</span>                    
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">signal</span> <span class="o">&gt;</span> <span class="n">Threshold</span> <span class="o">&amp;&amp;</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">    <span class="n">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">&#34;b&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl">    <span class="n">counter</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">counter</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">signal</span> <span class="o">&lt;</span> <span class="n">Threshold</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl">    <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl">  <span class="nf">delay</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Once that is uploaded, you can keep the Arduino connected and move on to the software.</p>
<h2 id="setup-the-software">Setup the software.</h2>
<p>First make sure you have Go installed on your computer You can <a href="https://golang.org/dl/" target="_blank" >download Go here</a>. Now you can go into the <code>heartbpm</code> code (download <a href="https://github.com/schollz/heartbpm" target="_blank" >on my Github</a>) and simply run in a terminal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">$ go build -v
</span></span></code></pre></div><p>Now you&rsquo;ll have an executable <code>heartbpm</code> in that directory. You can simply run it with</p>
<pre tabindex="0"><code>$ ./heartbpm --com COMPORT
[info]  2020/06/13 07:48:06 listening on :8054
</code></pre><p>Make sure you find your <code>COMPORT</code>. The easiest way to find your com port is to look at it from the Arduino IDE.</p>
<center>
<img alt="Finding the name of the COM port, here it is 'COM3'." src="/img/comport.jpg" width="100" style="max-width: 500px;"/>
</center>
<p>Now, connect the pulse sensor to your finger and open a web browser to http://localhost:8054. You should be able to see some data coming out.</p>
<h2 id="just-add-synthesizers">Just add synthesizers.</h2>
<p>To get it working with synthesizers, just attach your synthesizer via MIDI USB to your computer. Most modern synthesizers have USB which doubles as a MIDI connection. The website will <em>automatically</em> detect the synthesizers, so just attach them and reload the page. Then you will see which ones are attached.</p>
<p><img src="/img/heratbpmscreen.png" alt="Screenshot of gathering data with NTS-1 attached"></p>
<p>The website is getting the averaged pulse data calculated BPM. This BPM is used to send MIDI clock signals every 1/24th of a quarter note, which is the standard for setting tempos on devices.</p>
<p>That&rsquo;s it! I mentioned it was simple ;)</p>
<h2 id="useful-notes">Useful notes.</h2>
<ul>
<li>If you are having trouble getting your pulse connected, try moving the pulse sensor just a little bit (1/8&quot;). Sometimes if its not over your vein it will be harder to detect. Once its in the right spot it will stay there nicely, though!</li>
<li>If you are getting background noise its because the synthesizers are powered off your computer which (unless is battery operated) tends to have a noisy power supply. I highly recommend getting an <a href="https://www.amazon.com/gp/product/B06XQYN77L/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B06XQYN77L&amp;linkCode=as2&amp;tag=scholl-20&amp;linkId=ef5f1fd6a1f83b3fbbcabd242e4ff4cc" target="_blank" >audio isolator</a> to remove this noise.</li>
</ul>
<h2 id="enjoy">Enjoy!</h2>
<p>Hope this is useful for you, and hope you can create something new! If you are interested in the music I&rsquo;ve created, check out <a href="https://infinitedigits.bandcamp.com" target="_blank" >my Bandcamp</a> or just search <code>infinite digits</code> on any streaming platform.</p>
]]></description>
    </item>
    
    <item>
	    <title>op1z.com</title>
	    <image>https://schollz.com/img/mcop1.jpg</image>
      <link>/tinker/op1/</link>
      <pubDate>Sat, 16 May 2020 13:54:02 -0700</pubDate>
      
      <guid>/tinker/op1/</guid>
      <description><![CDATA[<p>TLDR: I reverse engineered the Teenage Engineering OP-1 drum patch so that I could make my own custom patches automatically. The website that resulted from this is <a href="https://teoperator.com" target="_blank" >teoperator.com</a>.</p>
<p>I do not work at <a href="https://teenage.engineering/" target="_blank" >Teenage Engineering</a>.</p>
<p>Nor do I know anything about audio file formats.</p>
<p>Despite this, I managed to reverse engineer one of the audio file formats for the <a href="https://teenage.engineering/products/op-1" target="_blank" >OP-1 synthesizer</a>, made by Teenage Engineering.</p>
<p>The end result is a nice little website that lets you build patches for the OP-1 sampler: <a href="https://teoperator.com" target="_blank" >https://teoperator.com</a>. All the code I talk about here is open-source and also available on <a href="https://github.com/schollz/teoperator" target="_blank" >Github</a>.</p>
<p>The rest of this blog is the story of how I got there.</p>
<h3 id="too-many-words-too-many-samples">too many words, too many samples</h3>
<p>The OP-1 is a wonderful synthesizer capable of a great many things. One of the great things about it is a sampler, namely the <a href="https://teenage.engineering/guides/op-1/drum-mode" target="_blank" >Drum sampler engine</a>. This sampler allows you to record a maximum 12 seconds of sound which can then be spliced between any two points. These splices can then be assigned to one of the 24 keys of the synth for easy playback. Normally the sampler is used for drums, but I like to use it for spoken word or poetry.</p>
<p><img src="/img/sample.png" alt="Example of cutting a sample in the OP-1 Drum sampler engine"></p>
<p>In fact, I like to use it for <em>lots</em> of spoken word and poetry. <a href="https://alsep12.com/" target="_blank" >My last album</a> had over 30 minutes of NASA recordings. My current album is sampling an hour of poetry readings. This ends up being <em>hundreds</em> of 12-second samples that need to be spliced and cut using the OP-1 Drum sampler engine.</p>
<p>For each 12-second sample I need to record it into the OP-1 via the line-in. Recording each one takes at least about a minute to set the levels and find the right position. After recording I work to splice it, which takes another minute or two. This is a easy workflow for a few 12-second samples. But for <em>hundreds</em> of 12-second samples, I&rsquo;m looking at <em>hours and hours</em> of work.</p>
<p>I believe in this case that my time to program some software for this task is less than time of work of the task. ..which means: time to do some automation!</p>
<h3 id="ffmpeg-is-magic">ffmpeg is magic</h3>
<p>The first thing I do with a sample from <code>file.mp3</code> is to convert it to the right OP-1 file type and truncate to 12 seconds (the max for the Drum sampler engine of the OP-1).</p>
<p>I know that the OP-1 has a special file type - the <code>.aif</code> file, which is popular for samplers. All I had to do was extract 12 seconds of audio and convert it into an <code>.aif</code> file. This is easily done with one <code>ffmpeg</code> command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl">ffmpeg -i file.mp3 -c copy -ss 00:00:00 -to 00:00:11.5 patch.aif
</span></span></code></pre></div><p>Next, to automatically generate key assignments I want to splice the sound. One way to splice is to use the transients, or when silence <em>ends</em>. Turns out that <code>ffmpeg</code> does this too! I wrote some code that runs <code>ffmpeg</code> for printing out places where it detects silence. The <code>ffmpeg</code> command for detecting silence, for <code>file.mp3</code> for silence of at least 0.2 seconds at -22db is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">ffmpeg -i patch.aif -af <span class="nv">silencedetect</span><span class="o">=</span><span class="nv">noise</span><span class="o">=</span>-22db:d<span class="o">=</span>0.2 -f null -
</span></span></code></pre></div><style>
img.special {
background: #5f93a0aa;
}
</style>
<p>I can then use a fancy tool like <code>audiowaveform</code> to make an image of the waveform and use <code>imagemagick</code> to color code it so I can visualize the splicings. So a given segment of audio might look like this after splitting on the silence:</p>
<a href="https://teoperator.com/patch?audioURL=https%3A%2F%2Fcdn.loc.gov%2Fservice%2Fgdc%2Fgdcarpl%2Fgdcarpl-1624415%2F1624415.mp3&secondsStart=992.5&secondsEnd=1005" target="_blank">
<img alt="ffmpeg splitting audio on silence" src="/img/waveform.png" class="special">
</a>
<p>Great, now I have an OP-1 patch, <code>patch.aif</code>, that is the current length and format for uploading to the Drum engine sampler. The next step was to set the metadata of the <code>patch.aif</code> file so that it contains information that the OP-1 Drum sampler engine can use to assign the keys to each of the splices.</p>
<h3 id="xxd-to-the-rescue">xxd to the rescue</h3>
<p>The injection of metadata into my new <code>patch.aif</code> requires a bit of sleuthing/research because I don&rsquo;t know how <code>.aif</code> files work. There is a <a href="http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/Docs/AIFF-1.3.pdf" target="_blank" >file spec from 1988</a> but to be perfectly honest I found everything I need to know using the magic unix tool, <code>xxd</code>, and some second-rate guessing.</p>
<p>The <code>xxd</code> tool allows you to visualize a hexdump of any file. So I used it to look at a normal <code>.aif</code> file and compare it to a OP-1 <code>.aif</code> file (which I downloaded from the synthesizer).</p>
<p>The first 30 bytes from a normal non-OP-1 <code>.aif</code> file looks like this:</p>
<pre tabindex="0"><code>$ xxd file.aif | head -n 3
00000000: 464f 524d 0008 c4ee 4149 4646 434f 4d4d  FORM....AIFFCOMM
00000010: 0000 0012 0002 0002 3130 0010 400e bb80  ........10..@...
00000020: 0000 0000 0000 5353 4e44 0008 c4c8 0000  ......SSND......
</code></pre><p>Obviously there are headers in the header (<code>FORM</code>,<code>AIFF</code>,<code>COMM</code>,<code>SSND</code>). I&rsquo;m guessing that <code>SSND</code> is the PCM data. <code>FORM</code> seems special, because it has four bytes right after it. For that particular file, I converted those bytes to decimal (<code>0008 c4ee</code>) and they corresponded to the file size minus 8 bytes, so I assume it is just a file sizer.</p>
<p>Now here&rsquo;s a truncated version of the OP-1 <code>.aif</code> file:</p>
<pre tabindex="0"><code>$ xxd op1.aif
00000000: 464f 524d 000f 4e6e 4149 4643 4656 4552  FORM..NnAIFCFVER
00000010: 0000 0004 a280 5140 434f 4d4d 0000 0040  ......Q@COMM...@
00000020: 0001 0007 a49c 0010 400e ac44 0000 0000  ........@..D....
00000030: 0000 736f 7774 2953 6967 6e65 6420 696e  ..sowt)Signed in
00000040: 7465 6765 7220 286c 6974 746c 652d 656e  teger (little-en
00000050: 6469 616e 2920 6c69 6e65 6172 2050 434d  dian) linear PCM
00000060: 4150 504c 0000 04c6 6f70 2d31 7b22 6472  APPL....op-1{&#34;dr
00000070: 756d 5f76 6572 7369 6f6e 223a 322c 2264  um_version&#34;:2,&#34;d
...
000004f0: 3139 322c 3831 3932 2c38 3139 322c 3831  192,8192,8192,81
00000500: 3932 2c38 3139 322c 3831 3932 2c38 3139  92,8192,8192,819
00000510: 322c 3831 3932 2c38 3139 322c 3831 3932  2,8192,8192,8192
00000520: 2c38 3139 322c 3831 3932 5d7d 0a20 5353  ,8192,8192]}. SS
00000530: 4e44 000f 4940 0000 0000 0000 0000 0000  ND..I@..........
00000540: 0000 0000 f5ff e0ff bcff 90ff 57ff 14ff  ............W...
</code></pre><p>You&rsquo;ll see that its got an <code>AIFC</code> (compressed format) and not <code>AIFF</code>, but that won&rsquo;t matter, since my <code>ffmpeg</code> converted ones aren&rsquo;t compressed. The big difference is that there is some JSON data, following the <code>APPL</code> tag. There are four bytes after the <code>APPL</code> which again corresponded exactly to the size until the <code>SSND</code> tag. This looks easy enough, all I have to do is insert <code>APPL</code>, then four bytes of size, then <code>op-1</code>, then my JSON data - right before the <code>SSND</code> tag.</p>
<p>When I did that, though, I corrupted my OP-1 sounds a few times. So don&rsquo;t do that! It turns out there are bytes right before the <code>SSND</code> tag and right after the JSON closing bracket that are important. In opening several OP-1 files I noticed sometimes it was <code>0a20</code> or sometimes just <code>0a</code>. I theorized that maybe it needs to keep blocks consistent, probably in multiples of 4 (because otherwise you would only have 1 byte or 0, not 2 or 1).</p>
<p>That works! So, after injecting the OP-1 meta data you have to insert <code>0a</code> or <code>20</code> until the total size of the file is a multiple of 4. (Maybe this is bullshit, someone please let me know).</p>
<h3 id="op-1-meta-data">op-1 meta data</h3>
<p>Finally, now that I know how to inject OP-1 data into a <code>.aif</code> file I just have to figure out what the OP-1 data will be.</p>
<p>My inspection of the <code>.aif</code> files with <code>xxd</code> reveals that the OP-1 metadata itself looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a></span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a></span><span class="cl">	<span class="s2">&#34;drum_version&#34;</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a></span><span class="cl">	<span class="s2">&#34;type&#34;</span><span class="o">:</span> <span class="s2">&#34;drum&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a></span><span class="cl">	<span class="s2">&#34;name&#34;</span><span class="o">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a></span><span class="cl">	<span class="s2">&#34;octave&#34;</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a></span><span class="cl">	<span class="s2">&#34;pitch&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">6144</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a></span><span class="cl">	<span class="s2">&#34;start&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">35186754</span><span class="p">,</span> <span class="mi">73270908</span><span class="p">,</span> <span class="mi">193926863</span><span class="p">,</span> <span class="mi">262863847</span><span class="p">,</span> <span class="mi">282963028</span><span class="p">,</span> <span class="mi">327734734</span><span class="p">,</span> <span class="mi">374604417</span><span class="p">,</span> <span class="mi">422374972</span><span class="p">,</span> <span class="mi">456892160</span><span class="p">,</span> <span class="mi">477153660</span><span class="p">,</span> <span class="mi">548131809</span><span class="p">,</span> <span class="mi">570661720</span><span class="p">,</span> <span class="mi">597144106</span><span class="p">,</span> <span class="mi">696446963</span><span class="p">,</span> <span class="mi">726788489</span><span class="p">,</span> <span class="mi">830413096</span><span class="p">,</span> <span class="mi">918041142</span><span class="p">,</span> <span class="mi">955370511</span><span class="p">,</span> <span class="mi">1001935845</span><span class="p">,</span> <span class="mi">1053265249</span><span class="p">,</span> <span class="mi">1053265249</span><span class="p">,</span> <span class="mi">1053265249</span><span class="p">,</span> <span class="mi">1053265249</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a></span><span class="cl">	<span class="s2">&#34;end&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">35182696</span><span class="p">,</span> <span class="mi">73266850</span><span class="p">,</span> <span class="mi">193922805</span><span class="p">,</span> <span class="mi">262859789</span><span class="p">,</span> <span class="mi">282958970</span><span class="p">,</span> <span class="mi">327730676</span><span class="p">,</span> <span class="mi">374600359</span><span class="p">,</span> <span class="mi">422370914</span><span class="p">,</span> <span class="mi">456888102</span><span class="p">,</span> <span class="mi">477149602</span><span class="p">,</span> <span class="mi">548127751</span><span class="p">,</span> <span class="mi">570657662</span><span class="p">,</span> <span class="mi">597140048</span><span class="p">,</span> <span class="mi">696442905</span><span class="p">,</span> <span class="mi">726784431</span><span class="p">,</span> <span class="mi">830409038</span><span class="p">,</span> <span class="mi">918037084</span><span class="p">,</span> <span class="mi">955366453</span><span class="p">,</span> <span class="mi">1001931787</span><span class="p">,</span> <span class="mi">1053261191</span><span class="p">,</span> <span class="mi">1153253906</span><span class="p">,</span> <span class="mi">1153253906</span><span class="p">,</span> <span class="mi">1153253906</span><span class="p">,</span> <span class="mi">1153253906</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a></span><span class="cl">	<span class="s2">&#34;playmode&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a></span><span class="cl">	<span class="s2">&#34;reverse&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a></span><span class="cl">	<span class="s2">&#34;volume&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">9195</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">5190</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">4969</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">16384</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">8192</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a></span><span class="cl">	<span class="s2">&#34;dyna_env&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">8192</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-13"><a class="lnlinks" href="#hl-4-13">13</a></span><span class="cl">	<span class="s2">&#34;fx_active&#34;</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-14"><a class="lnlinks" href="#hl-4-14">14</a></span><span class="cl">	<span class="s2">&#34;fx_type&#34;</span><span class="o">:</span> <span class="s2">&#34;delay&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-15"><a class="lnlinks" href="#hl-4-15">15</a></span><span class="cl">	<span class="s2">&#34;fx_params&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="mi">8000</span><span class="p">],</span>
</span></span><span class="line"><span class="ln" id="hl-4-16"><a class="lnlinks" href="#hl-4-16">16</a></span><span class="cl">	<span class="s2">&#34;lfo_active&#34;</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-17"><a class="lnlinks" href="#hl-4-17">17</a></span><span class="cl">	<span class="s2">&#34;lfo_type&#34;</span><span class="o">:</span> <span class="s2">&#34;tremolo&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-4-18"><a class="lnlinks" href="#hl-4-18">18</a></span><span class="cl">	<span class="s2">&#34;lfo_params&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">16000</span><span class="p">,</span> <span class="mi">16000</span><span class="p">,</span> <span class="mi">16000</span><span class="p">,</span> <span class="mi">16000</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-4-19"><a class="lnlinks" href="#hl-4-19">19</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Looks easy enough! There is a <code>start</code> and <code>end</code> and there are 24 points in each array, which I&rsquo;m guessing will correspond to the 24 keys! I noticed that those numbers are absolutely huge, and if I set an end point to the very end it shows up as 2147483646 - way to big to correspond to the max number of seconds (~12) or milliseconds. I divided that number by the total number of samples - 44100 * 12 - which came out to 4057.98. Thats pretty close to 4058 which seems reasonable (albeit weird) so all I need to do is multiple the sample position by 4058.</p>
<p><em>*minutes pass*</em></p>
<p>Turns out I was wrong. Turns out when I did that, it didn&rsquo;t brick my OP-1 but the markers I thought I had set were completely off. After thinking some more about it I saw that there are a lot of <code>8192</code> in the OP-1 JSON data. I&rsquo;m guessing that this is the smallest number it can discern, so I changed all my markers so that they correspond to the closest multiple of 8192 (max of 13 bits).</p>
<p>And guess what&hellip;</p>
<h3 id="it-works">It works!</h3>
<p>Yes, it works!</p>
<p>So here is how to take any sound and make an OP-1 drum patch with automatic key assignments:</p>
<p>Use <code>ffmpeg</code> to truncate to ~12 seconds and convert a sound to <code>.aif</code>, use <code>ffmpeg</code> to find silence, generate OP-1 JSON using start/end points from silence (careful to only use 13 bits of precision), inject JSON into the <code>.aif</code> file before the <code>SSND</code> tag and update the filler and <code>FORM</code> bytes so its valid.</p>
<p>That&rsquo;s it! Its short enough to tell someone, not that I knew any of this before starting. And <em>now</em> I can write a program to do this automatically on <a href="https://teoperator.com/patch?audioURL=https%3A%2F%2Fcdn.loc.gov%2Fservice%2Fgdc%2Fgdcarpl%2Fgdcarpl-1624415%2F1624415.mp3&amp;secondsStart=862&amp;secondsEnd=1182" target="_blank" >dozens of samples simultaneously</a>.</p>
<p>My major takeaway from this project is that <code>ffmpeg</code> and <code>xxd</code> are extremely powerful and all I really needed to get my end result. And the best part is that I can run the code on <em>hours</em> of audio which will automatically generate perfect 12-second clips that can be loaded onto the OP-1 with sample key bindings.</p>
<p>And if you want to use my end result, its on the web now at <a href="https://teoperator.com" target="_blank" >https://teoperator.com</a> and all the source code is available at <a href="https://github.com/schollz/teoperator" target="_blank" >Github</a>. And if you want to listen to my music, check out <a href="https://infinitedigits.bandcamp.com/" target="_blank" >my Bandcamp</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>hostyoself.com</title>
	    <image>https://schollz.com/img/projects/hostyoself.jpg</image>
      <link>/tinker/hostyoself/</link>
      <pubDate>Wed, 13 May 2020 08:00:46 -0700</pubDate>
      
      <guid>/tinker/hostyoself/</guid>
      <description><![CDATA[<p>TLDR: You can open this website and host anything from the website: <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a>.</p>
<p><strong>What&rsquo;s the point of this?</strong> You can host a website! You can share a file! Anything you want, directly from your browser!</p>
<p><strong>How do I start web hosting?</strong> You will need to setup port forwarding, a dynamic DNS, name registration, MySQL, PHP, Apache and take a online course in Javascript.</p>
<p>Just <em>kidding</em>! You don&rsquo;t need any of that crap. Just goto <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a> drag and drop a folder, or select a file. That&rsquo;s literally it. Now you can host a website from your laptop or your phone or your smartwatch or your toaster.</p>
<p><strong>How is this possible?</strong> When the server you point at gets a request for a webpage, the server turns back and asks <em>you</em> for that content and will use what you provide for the original request.</p>
<p><strong>Seriously, how is this possible?</strong> The relay uses websockets in your browser to process GET commands.</p>
<p><strong>Won&rsquo;t my website disappear when I close my browser?</strong> Yep! There is a <a href="https://github.com/schollz/hostyoself#host-from-the-command-line" target="_blank" >command-line tool</a> that doesn&rsquo;t require a browser so it can run in the background if you need that. But yes, if your computer turns off then your site is down. Welcome to the joys of hosting a site on the internet.</p>
<p><strong>Won&rsquo;t I have to reload my browser if I change a file?</strong> Yep! Welcome to the joys of Javascript.</p>
<p><strong>What&rsquo;s the largest file I can host using this?</strong> <code>¯\_(ツ)_/¯</code></p>
<p><strong>Should I use this to host a website?</strong> Dear god yes.</p>
<p><strong>Does this use AI or blockchain?</strong> Sure, why not.</p>
<p><strong>Does it scale?</strong> Horizontally, or vertically? Probably neither!</p>
<p><strong>What inspired this?</strong> <a href="https://beakerbrowser.com/" target="_blank" >beaker browser</a>, <a href="https://ngrok.com/" target="_blank" >ngrok</a>, <a href="http://localhost.run/" target="_blank" >localhost.run</a>, <a href="https://github.com/alexellis/inlets" target="_blank" >inlets.dev</a>, Parks and Recreation.</p>
<p>Here&rsquo;s an example where I use <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a> to host itself. I use <code>wget</code> to download <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a> and then host <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a> from <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a>: <a href="https://hostyoself.com/hostyoself/" target="_blank" >hostyoself.com/hostyoself/</a>. Happy 9th Anniversary <a href="https://en.wikipedia.org/wiki/Inception" target="_blank" >Inception</a> :cake:!</p>
<p><img src="https://raw.githubusercontent.com/schollz/hostyoself/master/static/inception.gif" alt="Hosting hostyourself from hostyourself"></p>
<h2 id="host-from-the-browser">Host from the browser</h2>
<p>Open <a href="https://hostyoself.com" target="_blank" >hostyoself.com</a> and drag and drop a folder, or select a file. Your browser will host the files!</p>
<h2 id="host-from-the-command-line">Host from the command line</h2>
<p>You can host files directly from the terminal!</p>
<pre tabindex="0"><code>$ hostyoself host
https://hostyoself.com/confidentcat/
</code></pre><p>Now if you have a file in your folder <code>README.md</code> you can access it with the public URL <code>https://hostyoself.com/confidentcat/README.md</code>, directly from your computer!</p>
<p>If you&rsquo;re on a Mac, you can install with Homebrew:</p>
<pre tabindex="0"><code>brew tap schollz/homebrew https://github.com/schollz/homebrew-tap.git
brew install hostyoself
</code></pre><p>Or you can host your current directory using Docker:</p>
<pre tabindex="0"><code>$ docker run -v `pwd`:/data schollz/hostyoself
</code></pre><h2 id="run-your-own-relay">Run your own relay</h2>
<p>Want to run your own relay? Its easy.</p>
<pre tabindex="0"><code>$ hostyoself relay --url https://yoururl
</code></pre><h2 id="develop">Develop</h2>
<pre tabindex="0"><code>$ git clone https://github.com/schollz/hostyoself
$ cd hostyoself
$ go generate -v -x
$ go build -v
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Worker pools in Golang</title>
	    <image>https://schollz.com/img/golangworker.jpg</image>
      <link>/tinker/worker-pool/</link>
      <pubDate>Wed, 19 Feb 2020 07:33:45 -0800</pubDate>
      
      <guid>/tinker/worker-pool/</guid>
      <description><![CDATA[<p>In Go there is one boilerplate that I use more than any other - the worker pool. The worker pool that I like most is the one presented by <a href="https://gobyexample.com/worker-pools" target="_blank" >Go By Example</a> which I modify each time I use.</p>
<p>Since I like to have my own control over the worker pool I write the code each time. Each time it requires implementation of a worker function, and the job and result types. However, to make it easier, I&rsquo;ve boiled it down to a simple copy and paste with only <strong>5 steps of user input</strong>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="c1">// step 1: specify the number of jobs
</span></span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">numJobs</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="c1">// step 2: specify the job and result
</span></span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">job</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl"><span class="kd">type</span> <span class="nx">result</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">	<span class="nx">err</span> <span class="kt">error</span>
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl"><span class="nx">jobs</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">job</span><span class="p">,</span> <span class="nx">numJobs</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl"><span class="nx">results</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">result</span><span class="p">,</span> <span class="nx">numJobs</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl"><span class="nx">runtime</span><span class="p">.</span><span class="nf">GOMAXPROCS</span><span class="p">(</span><span class="nx">runtime</span><span class="p">.</span><span class="nf">NumCPU</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl"><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">NumCPU</span><span class="p">();</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl">	<span class="k">go</span> <span class="kd">func</span><span class="p">(</span><span class="nx">jobs</span> <span class="o">&lt;-</span><span class="kd">chan</span> <span class="nx">job</span><span class="p">,</span> <span class="nx">results</span> <span class="kd">chan</span><span class="o">&lt;-</span> <span class="nx">result</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl">		<span class="k">for</span> <span class="nx">j</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">jobs</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl">			<span class="c1">// step 3: specify the work for the worker 
</span></span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl"><span class="c1"></span>			<span class="kd">var</span> <span class="nx">r</span> <span class="nx">result</span>  				
</span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl">			
</span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl">			<span class="nx">results</span> <span class="o">&lt;-</span> <span class="nx">r</span>
</span></span><span class="line"><span class="ln" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a></span><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a></span><span class="cl">	<span class="p">}(</span><span class="nx">jobs</span><span class="p">,</span><span class="nx">results</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a></span><span class="cl"><span class="c1">// step 4: send out jobs
</span></span></span><span class="line"><span class="ln" id="hl-0-27"><a class="lnlinks" href="#hl-0-27">27</a></span><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">i</span><span class="o">:=</span><span class="mi">0</span><span class="p">;</span><span class="nx">i</span><span class="p">&lt;</span><span class="nx">numJobs</span><span class="p">;</span><span class="nx">i</span><span class="o">++</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-28"><a class="lnlinks" href="#hl-0-28">28</a></span><span class="cl">	<span class="nx">jobs</span> <span class="o">&lt;-</span> <span class="nx">job</span><span class="p">{}</span>
</span></span><span class="line"><span class="ln" id="hl-0-29"><a class="lnlinks" href="#hl-0-29">29</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-30"><a class="lnlinks" href="#hl-0-30">30</a></span><span class="cl"><span class="nb">close</span><span class="p">(</span><span class="nx">jobs</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-31"><a class="lnlinks" href="#hl-0-31">31</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-32"><a class="lnlinks" href="#hl-0-32">32</a></span><span class="cl"><span class="c1">// step 5: do something with results
</span></span></span><span class="line"><span class="ln" id="hl-0-33"><a class="lnlinks" href="#hl-0-33">33</a></span><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">numJobs</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-34"><a class="lnlinks" href="#hl-0-34">34</a></span><span class="cl">	<span class="nx">r</span> <span class="o">:=</span> <span class="o">&lt;-</span><span class="nx">results</span>
</span></span><span class="line"><span class="ln" id="hl-0-35"><a class="lnlinks" href="#hl-0-35">35</a></span><span class="cl">	<span class="k">if</span> <span class="nx">r</span><span class="p">.</span><span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-36"><a class="lnlinks" href="#hl-0-36">36</a></span><span class="cl">		<span class="c1">// do something with error
</span></span></span><span class="line"><span class="ln" id="hl-0-37"><a class="lnlinks" href="#hl-0-37">37</a></span><span class="cl"><span class="c1"></span>	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-38"><a class="lnlinks" href="#hl-0-38">38</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The main change is that I&rsquo;ve created a <code>job</code> and a <code>result</code> type which you can populate with anything you want exchanged between the worker and main thread. Also I use <code>runtime</code> to automatically utilize all the porcessors.</p>
<p>Hope that might work for you too!</p>
]]></description>
    </item>
    
    <item>
	    <title>ALSEP12</title>
	    <image>https://schollz.com/img/alsep12.jpg</image>
      <link>/music/alsep12/</link>
      <pubDate>Tue, 18 Feb 2020 14:52:24 -0800</pubDate>
      
      <guid>/music/alsep12/</guid>
      <description><![CDATA[<p>This album is available <a href="https://alsep12.com" target="_blank" >on every streaming platform</a>. You can also listen directly on this blog:</p>
<center>
<iframe style="border: 0; width: 100%; height: 150px;" src="https://bandcamp.com/EmbeddedPlayer/album=3205169430/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/" seamless><a href="http://infinitedigits.bandcamp.com/album/alsep12">ALSEP12 by infinite digits</a></iframe>
</center>
<h2 id="about-alsep12">About ALSEP12</h2>
<p>This album, <em>ALSEP12</em>, follows the NASA audio of the Apollo 12 mission. Apollo 12 was a 10-day mission launched on November 14th, 1969 and sent three American astronauts to the surface of the moon. It was crewed by Captain Pete Conrad, Captain Alan Bean, and Captain Richard Gordon. This was the first mission to setup a nuclear-powered ALSEP (Apollo Lunar Surface Experiments Package) which relayed thousands of messages about the seismic, solar, and magnetic activity on the moon until it was shut down in 1977.</p>
<p>The Apollo 12 activities are probably the most ignored all the Apollo missions. It was a mission that had adverse conditions right at the beginning when they were struck by lightning twice. Fortunately the shuttle continued into space and they were able to land, with astounding precision, directly into the target zone at the site of Surveyor 3 probe. From there they setup the ALSEP instrumentation and collected samples to bring back to earth. On the return home the crew was congratulated by President Nixon (who was at trying to negotiate a arms control treaty with Russia) who noted that Captain Conrad was the first to ever sing on the moon.</p>
<p>All spoken audio for this album comes directly from NASA audio available on The Internet Archive and directly from NASA. All music is original music created using a OP-1 synthesizer and mastered using Audacity. An instrumental version of this album is available here: <a href="https://infinitedigits.bandcamp.com/album/alsep12-instrumental" target="_blank" >https://infinitedigits.bandcamp.com/album/alsep12-instrumental</a>.</p>
<ul>
<li>“Apollo 12 : NASA : Free Download, Borrow, and Streaming.” Internet Archive, archive.org/details/Apollo12Audio/.</li>
<li>“Apollo 12, Pinpoint for Science.” CNN, NASA, <a href="https://www.c-span.org/video/?466171-1/apollo-12-pinpoint-science" target="_blank" >www.c-span.org/video/?466171-1/apollo-12-pinpoint-science</a>.</li>
<li>“Apollo Lunar Surface Journal.” NASA, NASA, history.nasa.gov/alsj/main.html.</li>
<li>Brumfiel, Geoff. “50 Years Ago, Americans Made The 2nd Moon Landing&hellip; Why Doesn&rsquo;t Anyone Remember?” NPR, NPR, 19 Nov. 2019, <a href="https://www.npr.org/2019/11/19/780602012/50-years-ago-americans-made-the-2nd-moon-landing-why-doesnt-anyone-remember" target="_blank" >www.npr.org/2019/11/19/780602012/50-years-ago-americans-made-the-2nd-moon-landing-why-doesnt-anyone-remember</a>.</li>
</ul>
<h2 id="why-i-made-alsep12">Why I made ALSEP12</h2>
<p>I am still pretty new to synthesizers so I took a naive approach to making this album. This last November I heard on the radio that it was the 50th anniversary of the Apollo 12 mission. I had been thinking about making soundtracks for a long time, so I thought to myself, what if someone made a soundtrack to the raw audio of the Apollo 12 mission?</p>
<p>It was easy to find the NASA audio - there are 300 hours of it on The Internet Archive. The tricky part was that I had to listen to a lot of it to find the parts that were evocative enough to design a soundscape for this album.</p>
<h2 id="how-i-made-alsep12">How I made ALSEP12</h2>
<p>To make this album I recorded everything using almost entirely built-in sound engines from the <a href="https://teenage.engineering/products/op-1" target="_blank" >Teenage Engineering OP-1</a> (the only two samples are of trumpets and a MOOG), recording it on the OP-1 and then &ldquo;performed&rdquo; it (i.e. used the live OP-1 effects / mastering knobs) into a Big Sky reverb which I fed Audacity where I had overlayed the audio I had cut from NASA. That&rsquo;s literally it, its a naive approach but it works for me since I don&rsquo;t have any DAW experience (another reason I love the OP-1 as my DAW). There are some &ldquo;making of&rdquo; videos for this album on IG @infinitedigits: <a href="https://www.instagram.com/p/B5VTAmdBdKq/" target="_blank" >https://www.instagram.com/p/B5VTAmdBdKq/</a>. I subscribe to a lot of open-source software ideas so I also tried making all my OP-1 patches available: <a href="https://op1.fun/users/yakczar" target="_blank" >https://op1.fun/users/yakczar</a></p>
<p>In this album I tried to make every song tell a story. For example, the first song &ldquo;Lift off&rdquo; evokes how the Apollo 12 space shuttle was hit by lightning as it was taking off, while the second song &ldquo;Landing&rdquo; revels in the excitement of the astronauts at getting a pinpoint landing on the moon. Overall, I tried to make the album so that the listener will feel inspired and uplifted since I feel that Apollo 12 mission is one of the most fun and inspiring NASA missions (though often overlooked).</p>
<p>This album is one of my favorite things I&rsquo;ve ever created and I actually listen to it every week all the way through because I love it so much (my philosophy in music is to create music that you yourself want to listen to).</p>
]]></description>
    </item>
    
    <item>
	    <title>Programming a registration bot</title>
	    <image>https://schollz.com/img/pottery.jpg</image>
      <link>/tinker/pottery/</link>
      <pubDate>Tue, 18 Feb 2020 08:16:31 -0800</pubDate>
      
      <guid>/tinker/pottery/</guid>
      <description><![CDATA[<p>I&rsquo;ve been waiting a long time to take a pottery class. In a city of almost 1 million people, there are surprisingly few pottery classes and there are even fewer pottery classes that happen outside working hours. One of the classes I knew about had only 12 available seats that instantly sold out when registration opened. In fact, I missed the registration for this class several times already.</p>
<p>I learned that the demand was so great for this pottery class that the website maintainers had to pick a random time to open registration to prevent the servers from crashing. Since I couldn&rsquo;t spend all day refreshing the pottery website to see if registration was available, I decided to write a program to help sign up for this class.</p>
<h2 id="a-script-to-alert-when-a-website-changes">A script to alert when a website changes</h2>
<p>I made sure not to miss the registration this time by writing a script that monitors the registration website and sends an alert to my phone when it changes (when registration opens). Their are tons of online services that do this, but they cost money if you want to check a site more often than once every 15 minutes. However, writing this code is incredibly easy and satisfying.</p>
<p>The script I wrote is in Python which does the image processing and the alerting. The website snapshots are taken with a Node script (using <a href="https://github.com/puppeteer/puppeteer" target="_blank" >puppeteer</a>) which is run from the Python script.</p>
<p>I learned some subtle things about website tracking while doing this - namely website scraping is easier if you can block ads and that SMTP is the best free notification service. More on that below, but if you just want the code, the script and the instructions for using it are on my Github: <a href="https://github.com/schollz/websitechanges" target="_blank" >https://github.com/schollz/websitechanges</a>.</p>
<h3 id="block-the-ads-before-taking-a-snapshot">Block the ads before taking a snapshot</h3>
<p>The screenshot of the website is downloaded by using <code>puppeteer</code>, which is very easy to do (its one of the <a href="https://github.com/puppeteer/puppeteer/blob/master/examples/screenshot.js" target="_blank" >examples</a>!).</p>
<p>However, one subtlety here is that I need to compare two screenshots in time for changes. Since ads can change every time you reload a page, I realized it is important to remove ads to get a reproducible view of the website. This is really easy to do with <code>puppeteer</code>. First download a <a href="https://github.com/StevenBlack/hosts" target="_blank" >hosts file</a> and then load it into the Node script:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="nx">hosts</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="c1">//now we read the host file
</span></span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">hostFile</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="s1">&#39;hosts&#39;</span><span class="p">,</span> <span class="s1">&#39;utf8&#39;</span><span class="p">).</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;\n&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl"><span class="kd">var</span> <span class="nx">hosts</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">hostFile</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">hostFile</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&#34;#&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">        <span class="k">continue</span>
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl">    <span class="kd">var</span> <span class="nx">frags</span> <span class="o">=</span> <span class="nx">hostFile</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">frags</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="nx">frags</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">===</span> <span class="s1">&#39;0.0.0.0&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl">        <span class="nx">hosts</span><span class="p">[</span><span class="nx">frags</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">trim</span><span class="p">()]</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And then in <code>puppeteer</code> you can block all the requests to everythin in the HOSTS file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="cm">/* ... puppeteer setup omitted */</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl"><span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">setRequestInterception</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl"><span class="nx">page</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;request&#39;</span><span class="p">,</span> <span class="nx">request</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl">    <span class="kd">var</span> <span class="nx">domain</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl">    <span class="kd">var</span> <span class="nx">frags</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">().</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">frags</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl">        <span class="nx">domain</span> <span class="o">=</span> <span class="nx">frags</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl">    <span class="c1">// just abort if found
</span></span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="nx">hosts</span><span class="p">[</span><span class="nx">domain</span><span class="p">]</span> <span class="o">===</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl">        <span class="nx">request</span><span class="p">.</span><span class="nx">abort</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl">        <span class="nx">request</span><span class="p">.</span><span class="k">continue</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>This way, all the ads are removed and you just get a blank space or no space where they were.</p>
<h3 id="smtp-is-the-easiest-cheapest-way-to-send-notifications">SMTP is the easiest, cheapest way to send notifications</h3>
<p>The pottery website registration could occur at anytime in the middle of the night (it ended up being at 4:43 AM). I needed a way that the website change could notify me, namely by sending a text message. I can use my phone to play a loud sound when the message arrives. But how to send a text message?</p>
<p>It turns out to be very easy! To send a notification to your phone, you simply send an email! Your phone provider usually supplies an email address for your phone. Here&rsquo;s the ones I know of:</p>
<ul>
<li>Verizon: <code>PHONENUMBER@vtext.com</code></li>
<li>Sprint: <code>PHONENUMBER@messaging.sprintpcs.com</code></li>
</ul>
<p>But, then, how do you send a email from a program? You can use email API service. However some of these, like mailgun, entice you with an offer of a free tier only to <a href="https://news.ycombinator.com/item?id=22192543" target="_blank" >later remove the free-tier</a>. But the alternative is easy, fast, and free.</p>
<p>The alternative is to use <em>SMTP</em> which is provided with almost any free email service. For example, you can use a new Gmail account with a random username and password. To enable SMTP in Gmail:</p>
<ol start="0">
<li>If you are using a remote server, <a href="#gmail-smtp-getting-blocked" >check this</a></li>
<li>Go to the &ldquo;Settings&rdquo;, e.g. click on the &ldquo;Gears&rdquo; icon and select &ldquo;Settings&rdquo;.</li>
<li>Click on &ldquo;Forwarding and POP/IMAP&rdquo;.</li>
<li>Enable &ldquo;IMAP Access&rdquo; and/or &ldquo;POP Download&rdquo;</li>
<li>Goto <a href="https://myaccount.google.com/lesssecureapps" target="_blank" >https://myaccount.google.com/lesssecureapps</a> and turn &ldquo;Allow less secure apps&rdquo; to &ldquo;ON&rdquo;.</li>
</ol>
<p>That&rsquo;s it! Now you can send emails in Python using a function like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl"><span class="kn">import</span> <span class="nn">smtplib</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl"><span class="kn">from</span> <span class="nn">email.mime.text</span> <span class="kn">import</span> <span class="n">MIMEText</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl"><span class="kn">from</span> <span class="nn">email.mime.image</span> <span class="kn">import</span> <span class="n">MIMEImage</span>
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl"><span class="kn">from</span> <span class="nn">email.mime.multipart</span> <span class="kn">import</span> <span class="n">MIMEMultipart</span>
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl"><span class="k">def</span> <span class="nf">send_email</span><span class="p">(</span><span class="n">smtpemail</span><span class="p">,</span> <span class="n">smtppass</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">subject</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">attachment</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl">    <span class="n">img_data</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">attachment</span><span class="p">,</span> <span class="s2">&#34;rb&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">    <span class="n">msg</span> <span class="o">=</span> <span class="n">MIMEMultipart</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl">    <span class="n">msg</span><span class="p">[</span><span class="s2">&#34;Subject&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">subject</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl">    <span class="n">msg</span><span class="p">[</span><span class="s2">&#34;From&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">smtpemail</span>
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl">    <span class="n">msg</span><span class="p">[</span><span class="s2">&#34;To&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">to</span>
</span></span><span class="line"><span class="ln" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a></span><span class="cl">    <span class="n">text</span> <span class="o">=</span> <span class="n">MIMEText</span><span class="p">(</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a></span><span class="cl">    <span class="n">msg</span><span class="o">.</span><span class="n">attach</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a></span><span class="cl">    <span class="n">image</span> <span class="o">=</span> <span class="n">MIMEImage</span><span class="p">(</span><span class="n">img_data</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">attachment</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a></span><span class="cl">    <span class="n">msg</span><span class="o">.</span><span class="n">attach</span><span class="p">(</span><span class="n">image</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a></span><span class="cl">    <span class="n">s</span> <span class="o">=</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTP</span><span class="p">(</span><span class="s2">&#34;smtp.gmail.com&#34;</span><span class="p">,</span> <span class="s2">&#34;587&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a></span><span class="cl">    <span class="n">s</span><span class="o">.</span><span class="n">ehlo</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-21"><a class="lnlinks" href="#hl-2-21">21</a></span><span class="cl">    <span class="n">s</span><span class="o">.</span><span class="n">starttls</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-22"><a class="lnlinks" href="#hl-2-22">22</a></span><span class="cl">    <span class="n">s</span><span class="o">.</span><span class="n">ehlo</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-23"><a class="lnlinks" href="#hl-2-23">23</a></span><span class="cl">    <span class="n">s</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="n">smtpemail</span><span class="p">,</span> <span class="n">smtppass</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-24"><a class="lnlinks" href="#hl-2-24">24</a></span><span class="cl">    <span class="n">s</span><span class="o">.</span><span class="n">sendmail</span><span class="p">(</span><span class="n">msg</span><span class="p">[</span><span class="s2">&#34;From&#34;</span><span class="p">],</span> <span class="n">msg</span><span class="p">[</span><span class="s2">&#34;To&#34;</span><span class="p">],</span> <span class="n">msg</span><span class="o">.</span><span class="n">as_string</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-2-25"><a class="lnlinks" href="#hl-2-25">25</a></span><span class="cl">    <span class="n">s</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
</span></span></code></pre></div><p>Now you can have your program send a notification to your phone, with an image of the changes.</p>
<h2 id="back-to-pottery">Back to Pottery</h2>
<p>I wrote this script the night before the pottery class registration was set to take place. The exact time the registration was set to open was random. But then, at 4:43 am, I got a notification:</p>
<p><img src="/img/pottery-message.jpg" alt="Notification on my phone after my script detected a change."></p>
<p>I checked the website and saw that indeed the registration had opened and I got myself registered!</p>
<p>Interestingly even though I thought I&rsquo;d be the first there were already two other people registered by the time I was done registering! That means I&rsquo;m not the first to do this type of thing for this particular class. But my code is open-source at <a href="https://github.com/schollz/websitechanges" target="_blank" >https://github.com/schollz/websitechanges</a> so I hope everyone else will have a chance to try it too.</p>
<br>
<hr>
<br>
<br>
<div id="gmail-smtp-getting-blocked">Gmail SMTP getting blocked</div>
<p>There is a caveat about using SMTP with Gmail. Gmail will tend to block SMTP access if you create the account on one computer and then use it on a remote server (like Digital Ocean).</p>
<p>To get around this, make sure to create the account on the remote server, if that&rsquo;s where you plan to use it.</p>
<p>To do that, SSH into the remote server using</p>
<pre><code>ssh -D 8123 -C -N user@remoteserver
</code></pre>
<p>The <code>-D</code> parameter will bind a SOCKS port to <code>8123</code>. Now goto Firefox settings and change the SOCKS port to <code>8123</code>. Now Firefox will use your remote server and you can setup SMTP remotely. Then change it back when you&rsquo;re done!</p>
]]></description>
    </item>
    
    <item>
	    <title>Share</title>
	    <image>https://schollz.com/img/projects/share.jpg</image>
      <link>/tinker/share/</link>
      <pubDate>Wed, 22 Jan 2020 07:50:07 -0700</pubDate>
      
      <guid>/tinker/share/</guid>
      <description><![CDATA[<p>TLDR: Check out the website at <a href="https://share.schollz.com" target="_blank" >share.schollz.com</a>.</p>
<p><code>share</code> is an <a href="https://github.com/schollz/share" target="_blank" >open-source</a> server where you can easily share files through the browser or the terminal. <code>share</code> is inspired by <a href="https://transfer.sh/" target="_blank" >transfer.sh</a> and <a href="https://send.firefox.com" target="_blank" >send.firefox.com</a> which also store files temporarily after uploading via the browser or command-line.</p>
<p>One main new feature in <code>share</code> specifically is that the files are stored for time <em>based on the file size</em>, so that smaller files will stay available longer (by default, the time to deletion is scaled so that 1 GB file will be deleted after 30 minutes). Another nice improvement is that each uploaded file gets a permalink based on the hash of file content, so you can easily share a unique content-addressable six-digit identifier instead of a long filename.</p>
<h2 id="usage">Usage</h2>
<p>Open the browser <a href="https://share.schollz.com">share.schollz.com</a> and upload a file.</p>
<p>Alternatively you can upload via the command-line:</p>
<p><strong>Upload a file</strong></p>
<pre tabindex="0"><code>$ curl -L --progress --upload-file README.md share.schollz.com
https://share.schollz.com/bemi4x/README.md
</code></pre><p>Use this <code>.bashrc</code>/<code>.zshrc</code> shortcut:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="nb">alias</span> <span class="nv">share</span><span class="o">=</span><span class="s1">&#39;f() { curl --progress-bar --upload-file &#34;$1&#34; https://share.schollz.com | tee /dev/null; echo };f&#39;</span>
</span></span></code></pre></div><p><strong>Download a file</strong></p>
<p>You can download the file with just the unique ID, or with the filename added. So each of these are identical:</p>
<pre tabindex="0"><code>$ curl -L share.schollz.com/bemi4x
$ curl -L share.schollz.com/bemi4x/README.md
</code></pre><p>or you can use <code>wget</code>:</p>
<pre tabindex="0"><code>$ wget --content-disposition share.schollz.com/bemi4x
</code></pre><h2 id="install">Install</h2>
<p>You can easily install and run <code>share</code> on your own computer or server. First, make sure to <a href="https://golang.org/dl/" target="_blank" >install Go</a>. Then clone the repo and generate the code and run.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">1</a></span><span class="cl">$ git clone https://github.com/schollz/share.git
</span></span><span class="line"><span class="ln" id="hl-4-2"><a class="lnlinks" href="#hl-4-2">2</a></span><span class="cl">$ <span class="nb">cd</span> share/
</span></span><span class="line"><span class="ln" id="hl-4-3"><a class="lnlinks" href="#hl-4-3">3</a></span><span class="cl">$ go generate
</span></span><span class="line"><span class="ln" id="hl-4-4"><a class="lnlinks" href="#hl-4-4">4</a></span><span class="cl">$ go build -v
</span></span><span class="line"><span class="ln" id="hl-4-5"><a class="lnlinks" href="#hl-4-5">5</a></span><span class="cl">$ ./share
</span></span></code></pre></div><p>Use the flags (see <code>share --help</code>) for setting the max directory size, max file size, port, etc.</p>
<h3 id="docker">Docker</h3>
<p>You can also easily install and run with Docker (an 8MB image!).</p>
<pre tabindex="0"><code>$ docker run -d -v `pwd`/data:/data -p 8222:8222 schollz/share
</code></pre><p>If you are running on a public server, be sure to include <code>-e url=https://YOURURL.com</code> when running with Docker so that it presents the right URL in HTTP responses.</p>
<h2 id="acknowledgements">Acknowledgements</h2>
<p>This is inspired by other great file transfer utilities and also utilizes <a href="https://gitlab.com/meno/dropzone" target="_blank" >Dropzone.js</a> and uses logos from <a href="http://logodust.com/" target="_blank" >Logodust</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Books I read (2019)</title>
	    <image>https://schollz.com/img/books2019.jpg</image>
      <link>/tinker/books-2019/</link>
      <pubDate>Sun, 19 Jan 2020 09:41:01 -0600</pubDate>
      
      <guid>/tinker/books-2019/</guid>
      <description><![CDATA[<p><a href="/blog/books-2018/" ><em>(click here to see the books I enjoyed last year)</em></a></p>
<p>This year, my favorite book was <strong>Shadow Divers</strong> by <em>Robert Kurson</em>. It is a non-fictional account of the discovery of a German World War II U-boat right off the coast of New Jersey. It has great story-telling as the narrative compels you with the story of these divers and their technology and their passion. I could not put it down, even though I initially had no interest in diving or WWII history.</p>
<p>I also got to read the next book by <em>Ted Chiang</em>, <strong>Exhalation</strong> which I loved. The short stories are again works of genius, just like the last one.</p>
<p>My other favorite book was <strong>Shoeless Joe</strong> by <em>W. P. Kinsella</em>, an old but great book. Its a fun to read and the 1989 movie based on the book is the rare book-to-movie that follows the plot of the book almost scene for scene.</p>
<p>The most influential books were first <strong>Napoleon&rsquo;s Buttons</strong> by <em>Penny Le Couteur</em>, which shows the amazing simplicity/complexity of chemicals and illustrates the haphazard way in which many compounds are discovered. Also, I loved <strong>Experimental Music</strong> by <em>Robert Nyman</em> which explored the experimental music scene influenced by John Cage and contemporaries - there are some crazy songs in here including one &ldquo;song&rdquo; that is played by walking into the hole of a whale. Also, the immense and intricate storytelling in <strong>Bad Blood</strong> by <em>John Carreyrou</em> is astonishing, even though the story is troubling.</p>
<p>The other books I read this year in no particular order are <strong>Endure</strong> by <em>Alex Hutchinson</em>, <strong>Terminal Alliance</strong> by <em>Jim C. Hines</em>, , <strong>Factfulness</strong> by <em>Hans Rosling</em>, <strong>The Pleasures of the Damned</strong> by <em>Charles Bukowski</em>, <strong>Final Gifts</strong> by <em>Maggie Callanan</em>, and <strong>The Atrocity Archives</strong> by <em>Charles Strauss</em>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Websockets</title>
	    <image>https://schollz.com/img/websocketsgolang.jpg</image>
      <link>/tinker/websockets-with-golang/</link>
      <pubDate>Thu, 09 Jan 2020 15:00:07 -0800</pubDate>
      
      <guid>/tinker/websockets-with-golang/</guid>
      <description><![CDATA[<p><a href="https://schollz.com/websites/" target="_blank" >Every website I&rsquo;ve built recently</a> has made use of websockets. The frontend is always Javascript and the backend is always Go. I&rsquo;ve now gotten used to a programming pattern that I&rsquo;ve been implementing over and over for doing websockets. There are a <em>lot</em> of ways to implement websockets in the frontend and backend, but here&rsquo;s what I like to use.</p>
<h2 id="frontend-code-for-websockets">Frontend code for websockets</h2>
<p>The frontend just consists of a few lines of Javascript. No need for externally libraries. Basically these functions are pulled straight from the <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket" target="_blank" >Web APIs for Websockets from MDN</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="kd">var</span> <span class="nx">socket</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="kr">const</span> <span class="nx">socketMessageListener</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl"><span class="kr">const</span> <span class="nx">socketOpenListener</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Connected&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;hello, server&#34;</span> <span class="p">}))</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl"><span class="kr">const</span> <span class="nx">socketErrorListener</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl"><span class="kr">const</span> <span class="nx">socketCloseListener</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">socket</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Disconnected.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl">    <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">origin</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s2">&#34;http&#34;</span><span class="p">,</span> <span class="s2">&#34;ws&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;/ws&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl">    <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebSocket</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">onopen</span> <span class="o">=</span> <span class="nx">socketOpenListener</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">onmessage</span> <span class="o">=</span> <span class="nx">socketMessageListener</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">onclose</span> <span class="o">=</span> <span class="nx">socketCloseListener</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="nx">socketErrorListener</span><span class="p">;</span>
</span></span><span class="line"><span class="ln" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a></span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a></span><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;load&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a></span><span class="cl">    <span class="nx">socketCloseListener</span><span class="p">();</span>
</span></span><span class="line"><span class="ln" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a></span><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>The program is started by calling the closing listener. Since websockets naturally uses <code>Keep-Alive</code> this code will automatically re-join broken connections so you don&rsquo;t have to code that yourself! Modern browser&rsquo;s are great eh.</p>
<h2 id="backend-code-for-websockets">Backend code for websockets</h2>
<p>There are basically two libraries for doing this in Go. Both of them are pretty much the same - zero dependencies, fast, they bind JSON, and they wrap the <code>http</code> std library.</p>
<p>There&rsquo;s the old and reliable <a href="https://github.com/gorilla/websocket" target="_blank" ><code>gorilla/websocket</code></a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="kd">var</span> <span class="nx">wsupgrader</span> <span class="p">=</span> <span class="nx">websocket</span><span class="p">.</span><span class="nx">Upgrader</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl">	<span class="nx">ReadBufferSize</span><span class="p">:</span>  <span class="mi">1024</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl">	<span class="nx">WriteBufferSize</span><span class="p">:</span> <span class="mi">1024</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl">	<span class="nx">CheckOrigin</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl">		<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl">	<span class="p">},</span>
</span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl"><span class="kd">func</span> <span class="nf">handleWebsocket</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl">	<span class="nx">c</span><span class="p">,</span> <span class="nx">errUpgrade</span> <span class="o">:=</span> <span class="nx">wsupgrader</span><span class="p">.</span><span class="nf">Upgrade</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl">	<span class="k">if</span> <span class="nx">errUpgrade</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl">		<span class="k">return</span> <span class="nx">errUpgrade</span>
</span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl">	<span class="k">defer</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a></span><span class="cl">	<span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a></span><span class="cl">		<span class="kd">var</span> <span class="nx">p</span> <span class="kd">interface</span><span class="p">{}</span>
</span></span><span class="line"><span class="ln" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a></span><span class="cl">		<span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ReadJSON</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a></span><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a></span><span class="cl">			<span class="nx">log</span><span class="p">.</span><span class="nf">Debug</span><span class="p">(</span><span class="s">&#34;read:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a></span><span class="cl">			<span class="k">break</span>
</span></span><span class="line"><span class="ln" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a></span><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a></span><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Debugf</span><span class="p">(</span><span class="s">&#34;recv: %v&#34;</span><span class="p">,</span> <span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-24"><a class="lnlinks" href="#hl-1-24">24</a></span><span class="cl">		<span class="nx">c</span><span class="p">.</span><span class="nf">WriteJSON</span><span class="p">(</span><span class="kd">struct</span><span class="p">{</span> <span class="nx">Message</span> <span class="kt">string</span> <span class="p">}{</span>
</span></span><span class="line"><span class="ln" id="hl-1-25"><a class="lnlinks" href="#hl-1-25">25</a></span><span class="cl">			<span class="s">&#34;hello, browser&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-26"><a class="lnlinks" href="#hl-1-26">26</a></span><span class="cl">		<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-1-27"><a class="lnlinks" href="#hl-1-27">27</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-28"><a class="lnlinks" href="#hl-1-28">28</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-29"><a class="lnlinks" href="#hl-1-29">29</a></span><span class="cl">	<span class="k">return</span>
</span></span><span class="line"><span class="ln" id="hl-1-30"><a class="lnlinks" href="#hl-1-30">30</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And there&rsquo;s the more recent <a href="https://github.com/nhooyr/websocket" target="_blank" ><code>nhooyr/websocket</code></a>, which I&rsquo;ve forked into  <a href="https://github.com/schollz/websocket" target="_blank" ><code>schollz/websocket</code></a> to remove all the test dependencies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl">	<span class="s">&#34;github.com/schollz/websocket&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl">	<span class="s">&#34;github.com/schollz/websocket/wsjson&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl"><span class="kd">func</span> <span class="nf">handleWebsocket</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl">	<span class="nx">c</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">websocket</span><span class="p">.</span><span class="nf">Accept</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">		<span class="k">return</span>
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl">	<span class="k">defer</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Close</span><span class="p">(</span><span class="nx">websocket</span><span class="p">.</span><span class="nx">StatusInternalError</span><span class="p">,</span> <span class="s">&#34;internal error&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-13"><a class="lnlinks" href="#hl-2-13">13</a></span><span class="cl">	<span class="nx">ctx</span><span class="p">,</span> <span class="nx">cancel</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nf">Context</span><span class="p">(),</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Hour</span><span class="o">*</span><span class="mi">120000</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-14"><a class="lnlinks" href="#hl-2-14">14</a></span><span class="cl">	<span class="k">defer</span> <span class="nf">cancel</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-15"><a class="lnlinks" href="#hl-2-15">15</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-16"><a class="lnlinks" href="#hl-2-16">16</a></span><span class="cl">	<span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-17"><a class="lnlinks" href="#hl-2-17">17</a></span><span class="cl">		<span class="kd">var</span> <span class="nx">v</span> <span class="kd">interface</span><span class="p">{}</span>
</span></span><span class="line"><span class="ln" id="hl-2-18"><a class="lnlinks" href="#hl-2-18">18</a></span><span class="cl">		<span class="nx">err</span> <span class="p">=</span> <span class="nx">wsjson</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">c</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">v</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-19"><a class="lnlinks" href="#hl-2-19">19</a></span><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-20"><a class="lnlinks" href="#hl-2-20">20</a></span><span class="cl">			<span class="k">break</span>
</span></span><span class="line"><span class="ln" id="hl-2-21"><a class="lnlinks" href="#hl-2-21">21</a></span><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-22"><a class="lnlinks" href="#hl-2-22">22</a></span><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Debugf</span><span class="p">(</span><span class="s">&#34;received: %v&#34;</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-23"><a class="lnlinks" href="#hl-2-23">23</a></span><span class="cl">		<span class="nx">err</span> <span class="p">=</span> <span class="nx">wsjson</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">c</span><span class="p">,</span> <span class="kd">struct</span><span class="p">{</span> <span class="nx">Message</span> <span class="kt">string</span> <span class="p">}{</span>
</span></span><span class="line"><span class="ln" id="hl-2-24"><a class="lnlinks" href="#hl-2-24">24</a></span><span class="cl">			<span class="s">&#34;hello, browser&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-25"><a class="lnlinks" href="#hl-2-25">25</a></span><span class="cl">		<span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-2-26"><a class="lnlinks" href="#hl-2-26">26</a></span><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-27"><a class="lnlinks" href="#hl-2-27">27</a></span><span class="cl">			<span class="k">break</span>
</span></span><span class="line"><span class="ln" id="hl-2-28"><a class="lnlinks" href="#hl-2-28">28</a></span><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-29"><a class="lnlinks" href="#hl-2-29">29</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-30"><a class="lnlinks" href="#hl-2-30">30</a></span><span class="cl">	<span class="k">if</span> <span class="nx">websocket</span><span class="p">.</span><span class="nf">CloseStatus</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="o">==</span> <span class="nx">websocket</span><span class="p">.</span><span class="nx">StatusGoingAway</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-2-31"><a class="lnlinks" href="#hl-2-31">31</a></span><span class="cl">		<span class="nx">err</span> <span class="p">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="ln" id="hl-2-32"><a class="lnlinks" href="#hl-2-32">32</a></span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-2-33"><a class="lnlinks" href="#hl-2-33">33</a></span><span class="cl">	<span class="nx">c</span><span class="p">.</span><span class="nf">Close</span><span class="p">(</span><span class="nx">websocket</span><span class="p">.</span><span class="nx">StatusNormalClosure</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-34"><a class="lnlinks" href="#hl-2-34">34</a></span><span class="cl">	<span class="k">return</span>
</span></span><span class="line"><span class="ln" id="hl-2-35"><a class="lnlinks" href="#hl-2-35">35</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The latter example will eventually handle <code>HTTP/2</code> and it seems the <code>gorilla/websocket</code> never will (unless they start updating it!).</p>
<h2 id="try-it">Try it!</h2>
<p>There is code for this on <a href="https://github.com/schollz/basicwebsocket" target="_blank" >my Github</a>.</p>
<style>
.highlight {
font-size: 90%
}
</style>
]]></description>
    </item>
    
    <item>
	    <title>Radio Transmissions</title>
	    <image>https://schollz.com/img/radiotransmissions.jpg</image>
      <link>/music/radio-transmissions/</link>
      <pubDate>Tue, 31 Dec 2019 07:49:36 -0700</pubDate>
      
      <guid>/music/radio-transmissions/</guid>
      <description><![CDATA[<p>My first album is a set of experimental easy-listening electronic music created using the op-1 synthesizer. The op-1 has a built-in radio that was used to sample snippets of broadcasts from public radio. These public radio snippets are taken randomly, usually between recording tracks, and they are meant to reflect a random sample of the world with regards to science, culture, and politics. In a sense, the entirety of this album &ldquo;radio transmissions&rdquo; is a musical readout of the current state of society during September and October of 2019.</p>
<p>You can listen to the album on <a href="https://infinitedigits.bandcamp.com/album/radio-transmissions" target="_blank" >Bandcamp</a>, or directly on this blog:</p>
<center>
<iframe style="border: 0; width: 80%; height: 350px;" src="https://bandcamp.com/EmbeddedPlayer/album=2428357056/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/" seamless><a href="http://infinitedigits.bandcamp.com/album/radio-transmissions">radio transmissions by infinite digits</a></iframe>
</center>
<h2 id="description-of-selected-tracks">Description of selected tracks</h2>
<p>&ldquo;Rose Torphy&rdquo;: an interview with Rose Torphy, a woman who joined the national park junior ranger program at the age of 103.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>&ldquo;How Clever&rdquo;: an interview with a researcher about saving the Florida grasshopper sparrows from extinction. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>&ldquo;A world elsewhere&rdquo;: an interview with legendary director peter brook about his adaption of Shakespeare to the stage. <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>&ldquo;Loved moons&rdquo;: an interview with scientist Dr. Scott Sheppard about cataloging Saturn&rsquo;s moons and his enthusiasm for studying space. <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>“The past is never dead&quot;: changing culture has influenced the Dixie classic fair in North Carolina to adopt a name that no longer associates itself with slavery. <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<p>&ldquo;Gen GND&rdquo;: schools in Seattle faced a walkout by students who were part of a global climate strike to spur action to avoid potential catastrophic effects from climate change. <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p>&ldquo;Once in a generation&rdquo;: a speech by Marie Yovanovitch, the former ambassador to Ukraine, speaking about her battle against corruption. <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>“103-year-old becomes grand canyon ranger.” NPR, NPR, 2019, <a href="https://www.npr.org/2019/03/01/699261929/103-year-old-becomes-grand-canyon-ranger" target="_blank" >www.npr.org/2019/03/01/699261929/103-year-old-becomes-grand-canyon-ranger</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Green, Amy. “endangered florida grasshopper sparrows released into the wild despite concerns.” NPR, NPR, 23 sept. 2019, <a href="https://www.npr.org/2019/09/23/763521144/endangered-florida-grasshopper-sparrows-released-into-the-wild-despite-concerns" target="_blank" >www.npr.org/2019/09/23/763521144/endangered-florida-grasshopper-sparrows-released-into-the-wild-despite-concerns</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Lunden, Jeff. “at 94, director peter brook is still asking the deep questions.” NPR, NPR, 6 oct. 2019, <a href="https://www.npr.org/2019/10/06/766836746/at-94-director-peter-brook-is-still-asking-the-deep-questions" target="_blank" >www.npr.org/2019/10/06/766836746/at-94-director-peter-brook-is-still-asking-the-deep-questions</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>“October 08, 2019 episode transcript | cbc radio.” cbcnews, cbc/radio canada, 9 oct. 2019, <a href="https://www.cbc.ca/radio/asithappens/as-it-happens-tuesday-edition-1.5313282/october-08-2019-episode-transcript-1.5315058" target="_blank" >www.cbc.ca/radio/asithappens/as-it-happens-tuesday-edition-1.5313282/october-08-2019-episode-transcript-1.5315058</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Brown, Keri. “renaming the dixie classic fair.” NPR, NPR, 12 oct. 2019, <a href="https://www.npr.org/2019/10/12/769688175/renaming-the-dixie-classic-fair" target="_blank" >www.npr.org/2019/10/12/769688175/renaming-the-dixie-classic-fair</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Brady, Jeff. “a rising generation asserts itself on climate change.” NPR, NPR, 20 sept. 2019, <a href="https://www.npr.org/2019/09/20/761916356/global-youth-climate-strike-expected-to-draw-large-crowds" target="_blank" >www.npr.org/2019/09/20/761916356/global-youth-climate-strike-expected-to-draw-large-crowds</a>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Inskeep, Steve. “Marie Yovanovitch&rsquo;s story.” NPR, NPR, 15 nov. 2019, <a href="https://www.npr.org/2019/11/15/779628866/marie-yovanovitchs-story" target="_blank" >www.npr.org/2019/11/15/779628866/marie-yovanovitchs-story</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Art</title>
	    <image>https://schollz.com/img/newyorker/art.jpg</image>
      <link>/newyorker/art/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/art/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/art.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Cruise</title>
	    <image>https://schollz.com/img/newyorker/cruise.jpg</image>
      <link>/newyorker/cruise/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/cruise/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/cruise.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Disruption</title>
	    <image>https://schollz.com/img/newyorker/auto.jpg</image>
      <link>/newyorker/disruption/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/disruption/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/auto.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Hannukah</title>
	    <image>https://schollz.com/img/newyorker/hannukah.jpg</image>
      <link>/newyorker/hannukah/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/hannukah/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/hannukah.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Long walks</title>
	    <image>https://schollz.com/img/newyorker/longwalks.jpg</image>
      <link>/newyorker/long-walks/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/long-walks/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/longwalks.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Santa</title>
	    <image>https://schollz.com/img/newyorker/santa.jpg</image>
      <link>/newyorker/santa/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/santa/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/santa.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Shoe</title>
	    <image>https://schollz.com/img/newyorker/shoe.jpg</image>
      <link>/newyorker/shoe/</link>
      <pubDate>Mon, 30 Dec 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/shoe/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on July 13th, 2019.</p>
<p>Declined by The New Yorker on September 10th, 2019.</p>
<p><img src="/img/newyorker/shoe.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Photos I took</title>
	    <image>https://schollz.com/img/2019/9.jpg</image>
      <link>/tinker/photos-2019/</link>
      <pubDate>Sun, 29 Dec 2019 09:41:01 -0600</pubDate>
      
      <guid>/tinker/photos-2019/</guid>
      <description><![CDATA[<p>These photos narrate pieces of my life from 2019.</p>
<p>For instance, I still have a cat and he really wanted to go outside during the snowstorm of Seattle.</p>
<p><img src="/img/2019/1.jpg" alt="My cat watching the snow."></p>
<p>I set up a TIRF laser system at work and it is finally aligned and ready to image.</p>
<p><img src="/img/2019/2.jpg" alt="Quad-laser system."></p>
<p>I went hiking to Mirror Lake on a beautiful day.</p>
<p><img src="/img/2019/3.jpg" alt="Mirror lake."></p>
<p>I went hiking at Lake Keecheelus and saw the stumps with faces.</p>
<p><img src="/img/2019/4.jpg" alt="The Bob Ross tree."></p>
<p>I found an alleyway that looked like a movie set.</p>
<p><img src="/img/2019/5.jpg" alt="Possible album cover alley way."></p>
<p>I went to Phoenix, Arizona and took into the cactus and sunshine.</p>
<p><img src="/img/2019/6.jpg" alt="Looking out at the desert."></p>
<p>I hiked up to the top of Silver Peak, after failing to find the correct trail the first time.</p>
<p><img src="/img/2019/7.jpg" alt="View from the top over the Northwest cascades."></p>
<p>I watched the Christmas parade of boats over Lake Union.</p>
<p><img src="/img/2019/8.jpg" alt="Boats and lights and music."></p>
<p>I took a picture of a lighthouse as the sun was setting.</p>
<p><img src="/img/2019/9.jpg" alt="A lighthouse in Discovery park."></p>
<p>I took better pictures than <a href="/blog/photos-2018" >last year</a>, here&rsquo;s hoping that next year will be better still.</p>
]]></description>
    </item>
    
    <item>
	    <title>Javascript for MIDI multiplexing.</title>
	    <image>https://schollz.com/img/midi2midi.jpg</image>
      <link>/tinker/midi2midi/</link>
      <pubDate>Wed, 18 Dec 2019 08:42:50 -0800</pubDate>
      
      <guid>/tinker/midi2midi/</guid>
      <description><![CDATA[<p>If you are viewing this website in Chrome, then you can use it to connect two MIDI keyboards. The previous sentence makes me feel like I&rsquo;m in the 21st century.</p>
<style>
p.text {
font-size: 2em;
padding: 0.1em;
}
select {
font-size: 1.5em;
padding: 0.5em;
max-width: 90%;
}
.midithing{
background-color: #fef7e0; padding:1em;margin-top:1em;margin-bottom:1em;border-radius: 25px;
}
</style>
<div class="midithing">
<p align="center" class="text" id="connected" style="padding-top: 0.5em;">connect</p>
 <div id="connectioninfo">
<p align="center">
<select id="controller">
<option>X</option>
</select>
</p>
<p align="center" class="text">to</p>
<p align="center">
<select id="controllee">
<option>Y</option>
</select>
</p>
</div>
<script src="https://schollz.github.io/midi2midi/midi2midi.js"></script>
</div>
<p>I&rsquo;m amazed that you can have <a href="https://www.w3.org/TR/webmidi/" target="_blank" >websites that fully interop with MIDI specs</a> (though <a href="https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess" target="_blank" >not compatiable on all browsers still</a>). I had attempted this kind of thing before using <a href="https://github.com/schollz/pianoai" target="_blank" >Go + PortMidi</a> but it was always tricky getting it to work on different operating systems. Since Chrome is easy to install everywhere, you can actually have chrome everywhere supporting your MIDI devices, with just a <a href="https://github.com/schollz/midi2midi" target="_blank" >little Javascript code</a>.</p>
<p>You can find the repo for this code here: <a href="https://github.com/schollz/midi2midi" target="_blank" >github.com/schollz/midi2midi</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>findabooktoread.com</title>
	    <image>https://schollz.com/img/catonbooks.png</image>
      <link>/tinker/nowwhatdoiread/</link>
      <pubDate>Sun, 25 Aug 2019 17:19:16 -0700</pubDate>
      
      <guid>/tinker/nowwhatdoiread/</guid>
      <description><![CDATA[<p>TLDR: Use the website at <a href="https://findabooktoread.com" target="_blank" >findabooktoread.com</a>.</p>
<p>What book are you reading <em>right now</em>? Do you <em>enjoy</em> it?</p>
<p>If you answer &ldquo;<em>yes</em>&rdquo; to either of those questions then stop reading this blog and go read your book!</p>
<p>If you answer &ldquo;<em>no</em>&rdquo; to either of those questions then its <em>time</em>. Its time to get a new book to read.</p>
<p>&ldquo;But,&rdquo; you profess, &ldquo;there is nothing to read.&rdquo; I believe you. I believe because you&rsquo;ve asked your friends, you&rsquo;ve searched Amazon.com, you&rsquo;ve checked Goodreads.com, you&rsquo;ve browsed the library shelves, the Barnes &amp; Noble shelves, and no single book has caught your attention. And, now, you&rsquo;ve rightfully concluded that there is <em>nothing to read</em>.</p>
<p>Don&rsquo;t give up the search, yet. There <em>is</em> a book out there just for you - a book that is so <em>good</em> that you won&rsquo;t put it down even when it&rsquo;s 11:30 pm and you have to wake up at 5 am for work.</p>
<p>How do I know there is a book out there for you? I know because I&rsquo;ve been in the same situation. I&rsquo;ve searched, and gotten tired of searching and gave up. But then I found a better way to search. This better search led me to a book out there, written just for me. This way to find books has worked not just once or twice but <em>every time</em>. Now, I&rsquo;m able to find an amazing book anytime I have the question: <strong>&ldquo;now, what do I read?&rdquo;</strong></p>
<h2 id="a-new-way-to-search-for-books">A new way to search for books</h2>
<p>How do I find my next books to read? I do this the same way everyone does in 2019: using a <em>web app</em>.</p>
<p>A web app is just a fancy name for a computer program that is accessible on the internet. A web app&rsquo;s computer program can hold information about millions of books inside its circuit brain. It can use its gigahertz processor to find the most relevant book suggestions and use the internet to display them on a website. Unlike your friends and friendly librarian, a web app can take any request, at any time, and give you an unbiased book suggestion in milliseconds.</p>
<p>I made such a web app with my computer. And, since my computer is not owned by Amazon / Barne&rsquo;s and Noble / Powells or other online booksellers, it won&rsquo;t try to manipulate you because it wants you to buy something.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Note: My web app <em>does</em> use Amazon Affiliate links which sends me money for link clicks. This money pays for hosting ($12/year) after which I direct all profits from to supporting the actual Amazon via the <a href="https://www.amazonconservation.org/" target="_blank" >Amazon Conservation Association</a>.</p>
<p>My web app is further optimized to only show the best books with similar traits and exclude books that are sequels or multiple books from the same author.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>The web app is called <a href="https://findabooktoread.com" target="_blank"><code>findabooktoread.com</code></a>. Go ahead, try it.</p>
<h2 id="an-example-of-using-a-hrefhttpsfindabooktoreadcom-target_blankemfindabooktoreadcomema">An example of using <a href="https://findabooktoread.com" target="_blank"><em>findabooktoread.com</em></a></h2>
<p>Here&rsquo;s an example from my life. For some reason, I read <a href="ttps://findabooktoread.com/book/2511219665" ><em>Boy&rsquo;s Life</em> by Robert McCammon</a>. I thought it was a biography but it turned out to have magic and horror. I don&rsquo;t normally read horror but I loved this book. I thought that there wouldn&rsquo;t be many books like this.</p>
<p>I used <a href="https://findabooktoread.com" target="_blank"><code>findabooktoread.com</code></a> and entered &ldquo;<em>Boy&rsquo;s Life</em>&rdquo; and found <a href="https://findabooktoread.com/book/471747102-weaveworld-by-clive-barker?q=weaveworld" target="_blank" ><em>Weaveworld</em> by Clive Barker</a>. This book blew me away and I liked it more than <em>Boy&rsquo;s Life.</em> But, after finishing <em>Weaveworld</em> I was again dismayed. It was so good and unique I thought I wouldn&rsquo;t find anything close to it again. But then, I asked the web app for books similar to <em>Weaveworld</em> and found <a href="https://findabooktoread.com/book/2364775265-the-library-at-mount-char-by-scott-hawkins" target="_blank" ><em>The Library at Mount Char</em> by Scott Hawkins</a>. This book was even better!</p>
<p>I still find great and relevant book suggestions using <a href="https://findabooktoread.com" target="_blank"><code>findabooktoread.com</code></a>. The last book I put in <em>The Library at Mount Char</em> and now I&rsquo;m currently reading <a href="https://findabooktoread.com/book/3143870535-kraken-by-china-mieville" target="_blank" ><em>Kraken</em> by China Miéville</a>, which I&rsquo;m enjoying.</p>
<p>This example led me down a particular set of genres, but I&rsquo;ve found the genre doesn&rsquo;t matter and I&rsquo;ve gotten good recommendations for all other sorts of books. I&rsquo;ve found book suggestions for all genres, as there as the web app doesn&rsquo;t have any bias towards a particular genre.</p>
<h2 id="the-cycle-continues">The cycle continues</h2>
<img src="/img/cat.png" style="max-width:100px; float:right;">
<p>Every book I read now gives me more books to read with the help of this web app, <a href="https://findabooktoread.com" target="_blank"><code>findabooktoread.com</code></a>. Though I made it mostly for myself, I&rsquo;m opening it up to the world so that you can find a book and support a charity at the same time.</p>
<p>Maybe it will work for you. Maybe it won&rsquo;t.</p>
<p>In any case, I hope that instead of asking <em>&ldquo;now, what do I read?&rdquo;</em> you can just say <em>&ldquo;now, I read.&rdquo;</em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Other book search engines are heavily biased. Despite searching for books like Weaveworld, Goodreads will often give you popular books (it constantly recommends Harry Potter to me) and Amazon will give you most sold books (which happens to be vampire romance). Of course, this has everything to do with how they monetize their site.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I optimized <a href="https://findabooktoread.com" target="_blank"><code>findabooktoread.com</code></a> for speed and simplicity. There are no sign-ins, there is no tracking, there is no Javascript, and there are no cookies. Ideally, you receive results in a fraction of a second and can just spend time looking into results.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>How to tunnel with SSH</title>
	    <image>https://schollz.com/img/tunnel.png</image>
      <link>/tinker/tunneling/</link>
      <pubDate>Fri, 23 Aug 2019 11:46:28 -0700</pubDate>
      
      <guid>/tinker/tunneling/</guid>
      <description><![CDATA[<p>Here&rsquo;s a simple trick for SSH tunneling, so that you can bind a local port to one on a server that
you have SSH access.</p>
<p>&ldquo;<em>What for?</em>&rdquo; you ask? Let&rsquo;s say your at work and you need to add port-forwarding to your home. In this case you need to remotely access
<code>192.168.0.1</code> on your LAN network at home. To make your LAN network available at at your work network, simply make a SSH tunnel.</p>
<p>You can make an SSH tunnel to capture port <code>80</code> on your <em>remote machine</em> and show it on port <code>9000</code> of your local machine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl">$ ssh -L 9000:localhost:80 user@yourhomeaddress
</span></span></code></pre></div><p>Pretty cool!</p>
<p>You can also use <code>ssh</code> to tunnel into sites that are blocked. Let say you are at work and
imgur.com is being blocked. To get around this we can create a tunnel through a server which
isn&rsquo;t in our network and thus can access Imgur.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">$ ssh -L 9000:imgur.com:80 user@yourhomeaddress
</span></span></code></pre></div>]]></description>
    </item>
    
    <item>
	    <title>Car</title>
	    <image>https://schollz.com/img/newyorker/car.jpg</image>
      <link>/newyorker/car/</link>
      <pubDate>Mon, 08 Jul 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/car/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on March 3rd, 2019.</p>
<p>Declined by The New Yorker on July 8th, 2019.</p>
<p><img src="/img/newyorker/car.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Eat and run</title>
	    <image>https://schollz.com/img/newyorker/eatandrun.jpg</image>
      <link>/newyorker/eat-and-run/</link>
      <pubDate>Mon, 08 Jul 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/eat-and-run/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on March 3rd, 2019.</p>
<p>Declined by The New Yorker on July 8th, 2019.</p>
<p><img src="/img/newyorker/eatandrun.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Netflix</title>
	    <image>https://schollz.com/img/newyorker/netflix.jpg</image>
      <link>/newyorker/netflix/</link>
      <pubDate>Mon, 08 Jul 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/netflix/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on March 3rd, 2019.</p>
<p>Declined by The New Yorker on July 8th, 2019.</p>
<p><img src="/img/newyorker/netflix.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Performance art</title>
	    <image>https://schollz.com/img/newyorker/performanceart.jpg</image>
      <link>/newyorker/performance-art/</link>
      <pubDate>Mon, 08 Jul 2019 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/performance-art/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on March 3rd, 2019.</p>
<p>Declined by The New Yorker on July 8th, 2019.</p>
<p><img src="/img/newyorker/performanceart.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Dockerfile for Golang apps</title>
	    <image>https://schollz.com/img/godocker.jpg</image>
      <link>/tinker/go-docker/</link>
      <pubDate>Fri, 05 Jul 2019 07:08:07 -0700</pubDate>
      
      <guid>/tinker/go-docker/</guid>
      <description><![CDATA[<p>Docker is a useful container but it is also useful as a way to easily start/restart programs on multiple systems - or a single system. Also, even if the program is a single binary, Docker can be nice because the <a href="https://hub.docker.com" target="_blank" >Docker Hub</a> can be used to automatically build programs which are then pulled for deployment.</p>
<p>I&rsquo;ve settled on a nice pattern for a very clean image that can be used for self-contained fat Go binaries. The image itself is basically the size of the Go binary.</p>
<p>Say you have a program in <code>github.com/yourname/yourprogram</code>. Go into that directory</p>
<pre tabindex="0"><code>$ cd $GOPATH/src/github.com/yourname/yourprogram
</code></pre><p>and create a file <code>Dockerfile</code> with the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-docker" data-lang="docker"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="c">###################################</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl"><span class="err"></span><span class="c"># 1. Build in a Go-based image   #</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl"><span class="err"></span><span class="c">###################################</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="s"> golang:1.12-alpine as builder</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl"><span class="err"></span><span class="k">RUN</span> apk add --no-cache git <span class="c1"># add deps here (like make) if needed</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="s"> /go/yourprogram</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl"><span class="err"></span><span class="k">COPY</span> . .<span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl"><span class="err"></span><span class="c"># any pre-requisities to building should be added here</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl"><span class="err"></span><span class="c"># RUN go generate</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl"><span class="err"></span><span class="k">RUN</span> go build -v<span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl"><span class="err"></span><span class="c">###################################</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl"><span class="err"></span><span class="c"># 2. Copy into a clean image     #</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl"><span class="err"></span><span class="c">###################################</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="s"> alpine:latest</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a></span><span class="cl"><span class="err"></span><span class="k">COPY</span> --from<span class="o">=</span>builder /go/yourprogram/yourprogram /yourprogram<span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a></span><span class="cl"><span class="err"></span><span class="k">VOLUME</span><span class="s"> /data</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a></span><span class="cl"><span class="err"></span><span class="c"># expose port if needed</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a></span><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="s"> 8000</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a></span><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;/yourprogram&#34;</span><span class="p">]</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a></span><span class="cl"><span class="err"></span><span class="c"># any flags here, for example use the data folder</span><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a></span><span class="cl"><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;--data-folder&#34;</span><span class="p">,</span><span class="s2">&#34;/data&#34;</span><span class="p">]</span> <span class="err">
</span></span></span></code></pre></div><p>Now, in the same directory as the <code>Dockerfile</code> you can build your image.</p>
<pre tabindex="0"><code>$ docker build -t yourprogram .
</code></pre><p>Now you have an image that uses a data folder. You can then run the image with persistent data using</p>
<pre tabindex="0"><code>$ docker run -d -v /path/to/data:/data -p 8001:8000 yourprogram
</code></pre><p>Now your image will be running and saving data in <code>/path/to/data</code> and be accessible by the local <code>8001</code> port.</p>
<p>Its easiset to do this in a Github repo and then attach that to Docker Hub which will automatically build your images. Then you never need to build, after each push to the Github repo you can automatically get the latest image using:</p>
<pre tabindex="0"><code>$ docker run -d -v /path/to/data:/data -p 8001:8000 yourname/yourprogram
</code></pre><p>Now you can utilize things like <code>docker stats</code> and <code>docker ps</code> to check on the status of all your services, and easily stop/start them with <code>docker stop/run</code>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Exhalation Stories</title>
	    <image>https://schollz.com</image>
      <link>/books/exhalation/</link>
      <pubDate>Mon, 24 Jun 2019 09:43:35 -0600</pubDate>
      
      <guid>/books/exhalation/</guid>
      <description><![CDATA[<p>This is Ted Chiang&rsquo;s second book and it is another masterpiece. It is such a great fiction book which incorporates ideas on time travel, entropy, quantum realities, AI and free will.</p>
<p>What I like about his approach on futurism is that he is very optimistic rather than pessimistic. Also I like that his stories centers around the characters in these science-fiction scenarios and not nessecarily (actually sometimes actively avoiding) whether the technology/extreme-science is actually good or bad for humanity.</p>
<p>Ted Chiang is a brilliant author and somehow he makes the reader feel smart also, which is an amazing quality.</p>
]]></description>
    </item>
    
    <item>
	    <title>Napoleon&#39;s Buttons</title>
	    <image>https://schollz.com</image>
      <link>/books/molecules/</link>
      <pubDate>Sun, 16 Jun 2019 16:29:18 -0700</pubDate>
      
      <guid>/books/molecules/</guid>
      <description><![CDATA[<p>This book titled &ldquo;Napoleon&rsquo;s Buttons: How 17 Molecules Changed History&rdquo; is about the sciene and history of important molecules like caffeine, soap, DDT, dyes. This history is thorough and well-researched. Most importantly though, the authors do not shy away from presenting actual chemical diagrams which I really enjoyed because it shows how some of the chemicals are so simple (chlorofluorocarbons) while others can be extremly complex.</p>
]]></description>
    </item>
    
    <item>
	    <title>Reveal</title>
	    <image>https://schollz.com</image>
      <link>/podcasts/reveal/</link>
      <pubDate>Sun, 16 Jun 2019 16:22:20 -0700</pubDate>
      
      <guid>/podcasts/reveal/</guid>
      <description><![CDATA[<p>I got introduced to this podcast from the NPR One app. Its from <em>The Center fo Investigative Journalism</em> which tackles a lot of policy issues like taxes, government abuse, and other civil injustice. The first episode I heard from it hooked me, one about <a href="https://www.revealnews.org/episodes/captain-boycott/" target="_blank" >Captain Boycott</a> which has the namesake of the boycott we do today.</p>
]]></description>
    </item>
    
    <item>
	    <title>SoundQs</title>
	    <image>https://schollz.com</image>
      <link>/podcasts/soundqs/</link>
      <pubDate>Sun, 16 Jun 2019 16:10:53 -0700</pubDate>
      
      <guid>/podcasts/soundqs/</guid>
      <description><![CDATA[<p>SoundQs is an informative podcast about the most frequently asked questions about Seattle. Its made by Seattlites for Seattlites. I learned a lot of interesting stuff from it. For instance I did not know that the &ldquo;Seattle Dog&rdquo; was a thing (its a hot dog with cream cheese). I also learned that Lake Washington also used to have house boats. And I learned that in the year 2019 there were only <em>three</em> speeding tickets for bicyclists. Makes me wonder who these three cyclists were, how fast were they going?</p>
<p>My only wish is that this podcast was slightly longer.</p>
]]></description>
    </item>
    
    <item>
	    <title>JTB</title>
	    <image>https://schollz.com</image>
      <link>/photos/about/</link>
      <pubDate>Sun, 16 Jun 2019 06:35:41 -0700</pubDate>
      
      <guid>/photos/about/</guid>
      <description><![CDATA[<p><img src="/img/jtb.jpg" alt="Me on the JTB"></p>
]]></description>
    </item>
    
    <item>
	    <title>Mason Lake hike</title>
	    <image>https://schollz.com</image>
      <link>/photos/masonlake/</link>
      <pubDate>Mon, 27 May 2019 07:35:26 -0700</pubDate>
      
      <guid>/photos/masonlake/</guid>
      <description><![CDATA[<p><img src="/img/masonlake.jpg" alt="Hiking to Mason Lake"></p>
]]></description>
    </item>
    
    <item>
	    <title>offlinenotepad.com</title>
	    <image>https://schollz.com/img/projects/offlinenotepad.jpg</image>
      <link>/tinker/offlinenotepad/</link>
      <pubDate>Sun, 26 May 2019 08:00:46 -0700</pubDate>
      
      <guid>/tinker/offlinenotepad/</guid>
      <description><![CDATA[<p><em>offlinenotepad</em> is an <a href="https://github.com/schollz/offlinenotepad" target="_blank" >open-source</a> offline note taking app. It is a browser-based offline-first notepad that securely syncs across your devices - including smartphones, laptops, and chromebooks. Ideally, its a minimalist note-writing experience that can be accessed anywhere, anytime.</p>
<p><strong>Offline-first.</strong> All information is stored as encrypted data in the browser. Saving, editing, viewing, and searching are all done on the client.</p>
<p><strong>Secure.</strong> offlinenotepad uses AES with the PBE algorithm (PBKDF2) with the <a href="https://github.com/brix/crypto-js" target="_blank" >crypto-js library</a> to encrypt data on the client and the server.</p>
<p><strong>Minimal.</strong> This offline notepad aims to do as much as possible with as little as possible.</p>
<p><strong>Publish.</strong> Any page can be &ldquo;published&rdquo; so that is accessible by anyone with a simple random link, like <a href="https://offlinenotepad.com/50e5791a" target="_blank" ><code>offlinenotepad.com/50e5791a</code></a>. The raw data can easily be easily cURLed by adding <code>/raw</code> to the end, e.g. <a href="https://offlinenotepad.com/50e5791a/raw" target="_blank" ><code>offlinenotepad.com/50e5791a/raw</code></a>.</p>
<p><strong>Code.</strong> If the title of any document contains a period (&quot;.&quot;) then it will force the editor to be monospace and it will show the plain text in the viewer instead of transformed Markdown to HTML.</p>
<p>This writing tool is largely based of its predecessors: <a href="https://cowyo.com" target="_blank" >cowyo</a> and <a href="https://rwtxt.com" target="_blank" >rwtxt.com</a> (both also available on Github).</p>
<h2 id="install">Install</h2>
<p>To run your own server for backing up notes you can simply install with Go.</p>
<pre tabindex="0"><code>$ git clone https://github.com/schollz/offlinenotepad
$ cd offlinenotepad
$ go generate -v -x
$ go build -v
</code></pre><p>And then you can run</p>
<pre tabindex="0"><code>$ ./offlinenotepad
</code></pre><p>Log into <code>localhost:8251</code> to see the site.</p>
<h3 id="docker">Docker</h3>
<p>Alternatively you can run with docker:</p>
<pre tabindex="0"><code>$ docker run -v /location/to/save/data:/data -p 8251:8251 schollz/offlinenotepad
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Using vim with Golang</title>
	    <image>https://schollz.com/img/vim-go.png</image>
      <link>/tinker/go-in-vim/</link>
      <pubDate>Thu, 09 May 2019 16:46:21 -0600</pubDate>
      
      <guid>/tinker/go-in-vim/</guid>
      <description><![CDATA[<p>Vim is a console-based text editor that has been around for <a href="https://en.wikipedia.org/wiki/Vim_%28text_editor%29" target="_blank" >27 years</a> (actually its a clone from vi that is <a href="https://en.wikipedia.org/wiki/Vi" target="_blank" >43 years old</a>). Today, <a href="https://stackoverflow.com/questions/597077/what-are-the-benefits-of-learning-vim" target="_blank" >people still use</a> <em>and enjoy</em> Vim.</p>
<p>Vim is not so bad when it comes to writing, but when it comes to coding there are arguably much sleeker GUI-based tools like Sublime, Visual Code, Atom, IntelliJ, etc.</p>
<p>And yet, as of 2018 there <a href="https://blog.golang.org/survey2018-results" target="_blank" >were still 17% of people using vim with Go</a>, although that number as been in decline. This is particularly surprising to me because vim lacks a lot of useful GUI features (minimap, easy navigation between files). Also surprising Golang is becoming more popular even though it is not free (and quite expensive).</p>
<p>Still, I tried doing some coding with Vim. And, I like it! I like it enough to do it a lot. The features that I thought I would miss - like autocompletion, and easy building/testing from command-line - are really easy to do in Vim. Its definitely now part of my toolset, especially when I just need to edit a single file.</p>
<p>If you want to try using Go with Vim, here&rsquo;s how you get started.</p>
<h2 id="instructions-for-setting-up-vim-with-go">Instructions for setting up Vim with Go</h2>
<p>There are multiple ways to do this, but I&rsquo;m going to just write about a simple, easy method I found here.</p>
<pre tabindex="0"><code>$ git clone https://github.com/fatih/vim-go.git ~/.vim/pack/plugins/start/vim-go
$ mkdir -p ~/.vim/colors
$ curl https://raw.githubusercontent.com/tomasr/molokai/master/colors/molokai.vim &gt; ~/.vim/colors/molokai.vim
</code></pre><p>Then, just create a <code>~/.vimrc</code> with the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vim" data-lang="vim"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="k">set</span> <span class="nx">nocompatible</span>                <span class="c">&#34; Enables us Vim specific features</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl"><span class="k">filetype</span> <span class="nx">off</span>                    <span class="c">&#34; Reset filetype detection first ...</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl"><span class="k">filetype</span> <span class="nx">plugin</span> <span class="nx">indent</span> <span class="nx">on</span>       <span class="c">&#34; ... and enable filetype detection</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl"><span class="k">set</span> <span class="nx">ttyfast</span>                     <span class="c">&#34; Indicate fast terminal conn for faster redraw</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl"><span class="k">set</span> <span class="nx">ttymouse</span><span class="p">=</span><span class="nx">xterm2</span>             <span class="c">&#34; Indicate terminal type for mouse codes</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl"><span class="k">set</span> <span class="nx">ttyscroll</span><span class="p">=</span><span class="m">3</span>                 <span class="c">&#34; Speedup scrolling</span>
</span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl"><span class="k">set</span> <span class="nx">laststatus</span><span class="p">=</span><span class="m">2</span>                <span class="c">&#34; Show status line always</span>
</span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl"><span class="k">set</span> <span class="nx">encoding</span><span class="p">=</span><span class="nx">utf</span><span class="m">-8</span>              <span class="c">&#34; Set default encoding to UTF-8</span>
</span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl"><span class="k">set</span> <span class="nx">autoread</span>                    <span class="c">&#34; Automatically read changed files</span>
</span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl"><span class="k">set</span> <span class="nx">autoindent</span>                  <span class="c">&#34; Enabile Autoindent</span>
</span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl"><span class="k">set</span> <span class="nx">backspace</span><span class="p">=</span><span class="nx">indent</span><span class="p">,</span><span class="nx">eol</span><span class="p">,</span><span class="nx">start</span>  <span class="c">&#34; Makes backspace key more powerful.</span>
</span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl"><span class="k">set</span> <span class="nx">incsearch</span>                   <span class="c">&#34; Shows the match while typing</span>
</span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl"><span class="k">set</span> <span class="nx">hlsearch</span>                    <span class="c">&#34; Highlight found searches</span>
</span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl"><span class="k">set</span> <span class="nx">noerrorbells</span>                <span class="c">&#34; No beeps</span>
</span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl"><span class="k">set</span> <span class="nx">number</span>                      <span class="c">&#34; Show line numbers</span>
</span></span><span class="line"><span class="ln" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a></span><span class="cl"><span class="k">set</span> <span class="nx">showcmd</span>                     <span class="c">&#34; Show me what I&#39;m typing</span>
</span></span><span class="line"><span class="ln" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a></span><span class="cl"><span class="k">set</span> <span class="nx">noswapfile</span>                  <span class="c">&#34; Don&#39;t use swapfile</span>
</span></span><span class="line"><span class="ln" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a></span><span class="cl"><span class="k">set</span> <span class="nx">nobackup</span>                    <span class="c">&#34; Don&#39;t create annoying backup files</span>
</span></span><span class="line"><span class="ln" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a></span><span class="cl"><span class="k">set</span> <span class="nx">splitright</span>                  <span class="c">&#34; Vertical windows should be split to right</span>
</span></span><span class="line"><span class="ln" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a></span><span class="cl"><span class="k">set</span> <span class="nx">splitbelow</span>                  <span class="c">&#34; Horizontal windows should split to bottom</span>
</span></span><span class="line"><span class="ln" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a></span><span class="cl"><span class="k">set</span> <span class="nx">autowrite</span>                   <span class="c">&#34; Automatically save before :next, :make etc.</span>
</span></span><span class="line"><span class="ln" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a></span><span class="cl"><span class="k">set</span> <span class="nx">hidden</span>                      <span class="c">&#34; Buffer should still exist if window is closed</span>
</span></span><span class="line"><span class="ln" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a></span><span class="cl"><span class="k">set</span> <span class="nx">fileformats</span><span class="p">=</span><span class="nx">unix</span><span class="p">,</span><span class="nx">dos</span><span class="p">,</span><span class="nx">mac</span>    <span class="c">&#34; Prefer Unix over Windows over OS 9 formats</span>
</span></span><span class="line"><span class="ln" id="hl-1-24"><a class="lnlinks" href="#hl-1-24">24</a></span><span class="cl"><span class="k">set</span> <span class="nx">noshowmatch</span>                 <span class="c">&#34; Do not show matching brackets by flickering</span>
</span></span><span class="line"><span class="ln" id="hl-1-25"><a class="lnlinks" href="#hl-1-25">25</a></span><span class="cl"><span class="k">set</span> <span class="nx">noshowmode</span>                  <span class="c">&#34; We show the mode with airline or lightline</span>
</span></span><span class="line"><span class="ln" id="hl-1-26"><a class="lnlinks" href="#hl-1-26">26</a></span><span class="cl"><span class="k">set</span> <span class="nx">ignorecase</span>                  <span class="c">&#34; Search case insensitive...</span>
</span></span><span class="line"><span class="ln" id="hl-1-27"><a class="lnlinks" href="#hl-1-27">27</a></span><span class="cl"><span class="k">set</span> <span class="nx">smartcase</span>                   <span class="c">&#34; ... but not it begins with upper case</span>
</span></span><span class="line"><span class="ln" id="hl-1-28"><a class="lnlinks" href="#hl-1-28">28</a></span><span class="cl"><span class="k">set</span> <span class="nx">completeopt</span><span class="p">=</span><span class="nx">menu</span><span class="p">,</span><span class="nx">menuone</span>    <span class="c">&#34; Show popup menu, even if there is one entry</span>
</span></span><span class="line"><span class="ln" id="hl-1-29"><a class="lnlinks" href="#hl-1-29">29</a></span><span class="cl"><span class="k">set</span> <span class="nx">pumheight</span><span class="p">=</span><span class="m">10</span>                <span class="c">&#34; Completion window max size</span>
</span></span><span class="line"><span class="ln" id="hl-1-30"><a class="lnlinks" href="#hl-1-30">30</a></span><span class="cl"><span class="k">set</span> <span class="nx">nocursorcolumn</span>              <span class="c">&#34; Do not highlight column (speeds up highlighting)</span>
</span></span><span class="line"><span class="ln" id="hl-1-31"><a class="lnlinks" href="#hl-1-31">31</a></span><span class="cl"><span class="k">set</span> <span class="nx">nocursorline</span>                <span class="c">&#34; Do not highlight cursor (speeds up highlighting)</span>
</span></span><span class="line"><span class="ln" id="hl-1-32"><a class="lnlinks" href="#hl-1-32">32</a></span><span class="cl"><span class="k">set</span> <span class="nx">lazyredraw</span>                  <span class="c">&#34; Wait to redraw</span>
</span></span><span class="line"><span class="ln" id="hl-1-33"><a class="lnlinks" href="#hl-1-33">33</a></span><span class="cl"><span class="c">
</span></span></span><span class="line"><span class="ln" id="hl-1-34"><a class="lnlinks" href="#hl-1-34">34</a></span><span class="cl"><span class="c">&#34; Enable to copy to clipboard for operations like yank, delete, change and put</span>
</span></span><span class="line"><span class="ln" id="hl-1-35"><a class="lnlinks" href="#hl-1-35">35</a></span><span class="cl"><span class="c">&#34; http://stackoverflow.com/questions/20186975/vim-mac-how-to-copy-to-clipboard-without-pbcopy</span>
</span></span><span class="line"><span class="ln" id="hl-1-36"><a class="lnlinks" href="#hl-1-36">36</a></span><span class="cl"><span class="k">if</span> <span class="nx">has</span><span class="p">(</span><span class="s1">&#39;unnamedplus&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-37"><a class="lnlinks" href="#hl-1-37">37</a></span><span class="cl">  <span class="k">set</span> <span class="nx">clipboard</span>^<span class="p">=</span><span class="nx">unnamed</span>
</span></span><span class="line"><span class="ln" id="hl-1-38"><a class="lnlinks" href="#hl-1-38">38</a></span><span class="cl">  <span class="k">set</span> <span class="nx">clipboard</span>^<span class="p">=</span><span class="nx">unnamedplus</span>
</span></span><span class="line"><span class="ln" id="hl-1-39"><a class="lnlinks" href="#hl-1-39">39</a></span><span class="cl"><span class="k">endif</span>
</span></span><span class="line"><span class="ln" id="hl-1-40"><a class="lnlinks" href="#hl-1-40">40</a></span><span class="cl"><span class="c">
</span></span></span><span class="line"><span class="ln" id="hl-1-41"><a class="lnlinks" href="#hl-1-41">41</a></span><span class="cl"><span class="c">&#34; This enables us to undo files even if you exit Vim.</span>
</span></span><span class="line"><span class="ln" id="hl-1-42"><a class="lnlinks" href="#hl-1-42">42</a></span><span class="cl"><span class="k">if</span> <span class="nx">has</span><span class="p">(</span><span class="s1">&#39;persistent_undo&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-43"><a class="lnlinks" href="#hl-1-43">43</a></span><span class="cl">  <span class="k">set</span> <span class="nx">undofile</span>
</span></span><span class="line"><span class="ln" id="hl-1-44"><a class="lnlinks" href="#hl-1-44">44</a></span><span class="cl">  <span class="k">set</span> <span class="nx">undodir</span><span class="p">=~</span><span class="sr">/.config/</span><span class="nx">vim</span><span class="sr">/tmp/</span><span class="nx">undo</span><span class="sr">//</span>
</span></span><span class="line"><span class="ln" id="hl-1-45"><a class="lnlinks" href="#hl-1-45">45</a></span><span class="cl"><span class="k">endif</span>
</span></span><span class="line"><span class="ln" id="hl-1-46"><a class="lnlinks" href="#hl-1-46">46</a></span><span class="cl"><span class="c">
</span></span></span><span class="line"><span class="ln" id="hl-1-47"><a class="lnlinks" href="#hl-1-47">47</a></span><span class="cl"><span class="c">&#34; Colorscheme</span>
</span></span><span class="line"><span class="ln" id="hl-1-48"><a class="lnlinks" href="#hl-1-48">48</a></span><span class="cl"><span class="k">syntax</span> <span class="nx">enable</span>
</span></span><span class="line"><span class="ln" id="hl-1-49"><a class="lnlinks" href="#hl-1-49">49</a></span><span class="cl"><span class="k">set</span> <span class="nx">t_Co</span><span class="p">=</span><span class="m">256</span>
</span></span><span class="line"><span class="ln" id="hl-1-50"><a class="lnlinks" href="#hl-1-50">50</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">rehash256</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-51"><a class="lnlinks" href="#hl-1-51">51</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">molokai_original</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-52"><a class="lnlinks" href="#hl-1-52">52</a></span><span class="cl"><span class="k">colorscheme</span> <span class="nx">molokai</span>
</span></span><span class="line"><span class="ln" id="hl-1-53"><a class="lnlinks" href="#hl-1-53">53</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-54"><a class="lnlinks" href="#hl-1-54">54</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-55"><a class="lnlinks" href="#hl-1-55">55</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_fmt_command</span> <span class="p">=</span> <span class="s2">&#34;goimports&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-1-56"><a class="lnlinks" href="#hl-1-56">56</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_autodetect_gopath</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-57"><a class="lnlinks" href="#hl-1-57">57</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_list_type</span> <span class="p">=</span> <span class="s2">&#34;quickfix&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-1-58"><a class="lnlinks" href="#hl-1-58">58</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-59"><a class="lnlinks" href="#hl-1-59">59</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_highlight_types</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-60"><a class="lnlinks" href="#hl-1-60">60</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_highlight_fields</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-61"><a class="lnlinks" href="#hl-1-61">61</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_highlight_functions</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-62"><a class="lnlinks" href="#hl-1-62">62</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_highlight_function_calls</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-63"><a class="lnlinks" href="#hl-1-63">63</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_highlight_extra_types</span> <span class="p">=</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-64"><a class="lnlinks" href="#hl-1-64">64</a></span><span class="cl"><span class="k">let</span> <span class="nx">g</span>:<span class="nx">go_highlight_generate_tags</span> <span class="p">=</span> <span class="m">1</span>
</span></span></code></pre></div><p>Then open up a <code>.go</code> file. <strong>Note</strong>: if you are not in a <code>GOPATH</code> or using a module, then some things like autocompletion will not work!</p>
<p>Hit <code>esc</code> and then type</p>
<pre tabindex="0"><code>:GoInstallBinaries
</code></pre><p>and press enter.</p>
<h2 id="using-go-in-vim">Using Go in Vim</h2>
<p>A lot of stuff is done automatically. For instance, whenever you save you will automatically be formatting with <code>goimports</code>.</p>
<p>To build a file you can save (<code>:w!</code>) and then you can run a program directly while editing using:</p>
<pre tabindex="0"><code>:! go build -v; ./program --debug
</code></pre><p>You can also use pre-built commands, say</p>
<pre tabindex="0"><code>:GoTest
</code></pre><p>You can pull up hints for functions by hitting <code>Ctl+X+O</code>.</p>
]]></description>
    </item>
    
    <item>
	    <title>croc</title>
	    <image>https://schollz.com/img/projects/croc.jpg</image>
      <link>/tinker/croc6/</link>
      <pubDate>Thu, 02 May 2019 07:02:12 -0700</pubDate>
      
      <guid>/tinker/croc6/</guid>
      <description><![CDATA[<p>There are a lot of ways to transfer files.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Above the basic necessity of preserving the data during the transfer, sharing data should be <em>fast</em>, <em>secure</em>, and &ndash; most of all &ndash; <em>easy</em>. Most file transfer utilities I&rsquo;ve used encompass two thirds of these qualities. I&rsquo;ve wanted to make a utility that encompasses all three, without compromising any one of them.</p>
<h2 id="relay--uploading">relay &gt; uploading</h2>
<p>File transfers should be as fast as possible - the time it takes to transfer the file is often spent waiting. Faster file transfers means less waiting.</p>
<p>A common way to transfer a file is to first upload data to a server, and then, once uploaded, the link is shared with someone who goes to download it.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> This method is easy, but slow &ndash; the transfer rate of the file is half the harmonic mean of the upload and download speeds which makes it slower than either just uploading or downloading.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>There are better ways than sequentially uploading and downloading. Instead, you can use a <strong>relay server</strong> to create a full-duplex real-time communication layer between the two computers so that &ldquo;uploading&rdquo; and &ldquo;downloading&rdquo; occur simultaneously between the two computers. This effectively increases the transfer rate because its not sequential and only limited by the slower of the two transfer modes (uploading or downloading).</p>
<h2 id="key-exchange--passwords">key exchange &gt; passwords</h2>
<p>Transferring data should be secure. Whenever you share data, it goes through a network of servers that can &ldquo;see&rdquo; the data, so it should be encrypted to stay private. A common way to do end-to-end encryption is with a password. You use a password to encrypt your file, then send the password-protected file, and after which the other person uses the same password to decrypt it.</p>
<p>Passwords are easy, but passwords can be weak. It turns out <a href="https://www.troyhunt.com/86-of-passwords-are-terrible-and-other-statistics/" target="_blank" >many people use the same passwords</a> so that breaking an encryption can be as easy as just trying <a href="https://haveibeenpwned.com/Passwords" target="_blank" >each password in a list of pwned accounts</a>.</p>
<p>Weak passwords can be used to make strong passwords using <a href="https://github.com/schollz/pake" target="_blank" >PAKE</a>: <em>password authenticated key exchange</em>. PAKE is a cryptographic method where two people share a password which is then used &ndash; via back-and-forth communication &ndash; to generate a strong key. The strong key can then be used for all further encryption. Since the two people generate the strong key by exchanging information, no one else could possibly learn the strong key even if they have the original password.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>There are other ways to exchange a key, but in general <em>you can get better security by exchanging a key rather than using a single password</em> on both computers.</p>
<h2 id="no-port-forwarding--port-forwarding">no port forwarding &gt; port forwarding</h2>
<p>Finally, transferring data should be simple, ideally with a single command and no maintenance or other programs needed.</p>
<p>Often it is not easy though.</p>
<p>Another common way to transfer a file is to use SSH or FTP. Both protocols are ubiquitous and relatively simple to use, but their simplicity relies on one of the computers to be running a server (like <code>openssh</code>) <strong>and</strong> it requires the server computer to have port forwarding enabled. It can be pretty much assumed that <em>most</em> pairs of computers don&rsquo;t meet these requirements (say, two computers in a café) so that makes these common utilities hard to use in most cases.</p>
<p>File transfers can be easier by eliminating the need for hosting a server or port forwarding. Again, using a <strong>relay server</strong> allows any two computers to connect to one another without resorting to port forwarding or fiddling with a server.</p>
<h2 id="croc--fast--secure--easy">croc = fast + secure + easy</h2>
<p>I&rsquo;ve been working on an open-source tool that takes these ideas to heart: <a href="https://github.com/schollz/croc" target="_blank" >croc</a>. Because its open-source and implemented in a language that easily cross compiles (Go) it can be implemented anywhere and thus will (hopefully) always be a fast, secure, and really easy way to send data.</p>
<p>AFAIK, <em>croc</em> is the only CLI file-transfer tool does <strong>all</strong> of the following:</p>
<ul>
<li>allows <strong>any two computers</strong> to transfer data (using a relay)</li>
<li>provides <strong>end-to-end encryption</strong> (using PAKE)</li>
<li>enables easy <strong>cross-platform</strong> transfers (Windows, Linux, Mac)</li>
<li>allows <strong>multiple file</strong> transfers</li>
<li>allows <strong>resuming transfers</strong> that are interrupted</li>
<li>does <em>not</em> require a server or port forwarding</li>
</ul>
<p>In developing <em>croc</em> I took a lot of inspiration from other CLI tools like <a href="https://github.com/zerotier/toss" target="_blank" >toss</a> and <a href="https://github.com/warner/magic-wormhole" target="_blank" >magic-wormhole</a> which had some but not all of the qualities above.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<p>If you&rsquo;d like to give it a try, the code is available online at <a href="https://github.com/schollz/croc" target="_blank" >https://github.com/schollz/croc</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I wrote about <a href="/software/sending-a-file/" >sending a file in 2017</a> and have tried to maintain a list of ways to send files since then.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Some good server-based file-sharing tools: <a href="https://send-anywhere.com" target="_blank" >https://send-anywhere.com</a>, <a href="https://send.firefox.com" target="_blank" >https://send.firefox.com</a>, <a href="https://ipfs.io/" target="_blank" >https://ipfs.io/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>For example, if you are transfering a file by uploading at 5 Mbit/s and then, after finishing the upload, downloading at 8 Mbit/s, then the effective transfer rate is 1/2 * 2/(1/5+1/8) = 3.1 Mbit/s - which is slower than either.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>PAKE additionally will prevent eavesdroppers. If anyone &ldquo;listens in&rdquo; on the information exchange, then all parties will end up with different strong keys and no one can decrypt anything between them, alerting the users that a eavesdropper is present.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://github.com/zerotier/toss" target="_blank" >toss</a> cleverly encodes port information in the code phrase, making it simple but it requires using connected computers (no firewalls) and the long random-ish code phrase is hard to &ldquo;tell&rdquo; someone. <a href="https://github.com/warner/magic-wormhole" target="_blank" >magic-wormhole</a> has most everything (currently its missing capabilities for multiple file transfers and file resuming), but it requires installing lots of the Python ecosystem which is tricky for non-developers (and Windows users).&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Terminal Alliance</title>
	    <image>https://schollz.com</image>
      <link>/books/alliance/</link>
      <pubDate>Wed, 17 Apr 2019 16:29:18 -0700</pubDate>
      
      <guid>/books/alliance/</guid>
      <description><![CDATA[<p>This one was recommended to me by a good friend because I liked a similar satirical (slightly) book called &ldquo;Red Shirts.&rdquo; This book basically is a story in Star Trek, except the crew is disabled and the mostly incompetent janitors work on fixing everything.</p>
<p>In general I thought this book did a good job of world-building (more show, less tell) and I thought the premise was strong and the execution was very good. It&rsquo;s definetly good enough that I am looking forward to getting the next book.</p>
]]></description>
    </item>
    
    <item>
	    <title>FIND</title>
	    <image>https://schollz.com/img/projects/find3.gif</image>
      <link>/tinker/find3/</link>
      <pubDate>Sun, 17 Mar 2019 09:41:01 -0600</pubDate>
      
      <guid>/tinker/find3/</guid>
      <description><![CDATA[<p>There is a new version of FIND now, called &ldquo;FIND3.&rdquo; Its public on <a href="https://github.com/schollz/find3" target="_blank" >Github</a> now.</p>
<p>FIND is the Framework for Internal Navigation and Discovery. It is basically an indoor GPS for your house or business, using only a simple smartphone or laptop.</p>
<p>This is an idea I started thinking about 8 years ago and  wrote the <a href="https://github.com/schollz/wifi_triangulation/tree/984ee1110e864b4bc4e751cab1a81c5688c3c0a6" target="_blank" >first code</a> in PHP almost four years ago, dubbed version 0, as a proof-of-concept. Two years later the project was <a href="https://github.com/schollz/find/tree/python3" target="_blank" >re-written in Python</a>. Then it was <a href="https://github.com/schollz/find" target="_blank" >re-written in Go</a> and called &ldquo;FIND&rdquo;. It&rsquo;s been <a href="https://github.com/kootenpv/whereami" target="_blank" >ported again to Python</a> and called &ldquo;whereami&rdquo;. Now its been <a href="https://github.com/schollz/find3" target="_blank" >re-written one more time and called &ldquo;FIND3&rdquo;</a>, with better speed, efficiency and new features.</p>
<p>Before getting to the benefits of the new version, I&rsquo;d like to highlight what FIND can be used for. My main use case is home automation, and I&rsquo;ve provided <a href="https://www.internalpositioning.com/doc/automation.md" target="_blank" >tutorials to get started with Home Assistant and OpenHAB</a>. In my home automation setup I use FIND to turn on/off lights as I walk around the house. In the future I&rsquo;d like to use FIND for more wayfinding in hospitals and hard to navigate locations.</p>
<p>Its been amazing that in the past few years to hear how other people are using FIND. Folks are using FIND to relay exhibit information in museums, as a safety monitor system for employees working alone, tracking number of attendees in a music festival, monitoring the location of pets, wayfinding in airports. I&rsquo;m hoping the new version will better support all these use cases and maybe more!</p>
<p>The new version of FIND has been written with longevity and flexibility in mind. There are notable improvements from the previous version:</p>
<ul>
<li><a href="https://www.internalpositioning.com/doc/passive_tracking.md" target="_blank" >Passive scanning</a> built-in (previously required a separate server, <a href="https://github.com/schollz/find-lf" target="_blank" >find-lf</a>).</li>
<li><a href="https://play.google.com/store/apps/details?id=com.internalpositioning.find3.find3app" target="_blank" >FIND3 app</a> and <a href="https://www.internalpositioning.com/doc/cli-scanner.md" target="_blank" >command-line tool</a> support Bluetooth scanning (previously just WiFi).</li>
<li>Meta-learning with 10 different <a href="https://www.internalpositioning.com/doc/overview.md#machine-learning" target="_blank" >machine learning classifiers</a> (previously just three).</li>
<li>Client uses Websockets which reduces bandwidth .</li>
<li><a href="https://github.com/schollz/stringsizer" target="_blank" >Rolling compression</a> of MAC addresses for much smaller on-disk databases.</li>
<li>Data storage in SQLite-database (previously it was BoltDB).</li>
<li>The API for sending fingerprints (/track and /learn) and MQTT endpoints are backward compatible.</li>
<li>Uses the MIT license (instead of incompatible AGPL)</li>
</ul>
<h3 id="getting-started">Getting started</h3>
<p>There are couple of ways you can get started with the new version.</p>
<p>The easiest way to get started is by <a href="https://www.internalpositioning.com/doc/tracking_your_phone.md" target="_blank" >tracking your phone</a>. Next, you could try <a href="https://www.internalpositioning.com/doc/tracking_your_computer.md" target="_blank" >tracking your computer</a>. If you have a couple of Raspberry Pi&rsquo;s laying around, you can try the more advanced <a href="https://www.internalpositioning.com/doc/passive_tracking.md" target="_blank" >passive tracking</a> which allows monitoring all nearby bluetooth and WiFi devices.</p>
<p>If you run into a problem with FIND, don&rsquo;t hesitate to <a href="https://github.com/schollz/find3/issues/new?template=bugs.md&amp;title=Bug:%20" target="_blank" >make a bug report</a>. For quick help, <a href="https://find3.slack.com/messages/C9H704GP4" target="_blank" >join the Slack channel</a> and someone there will help you (maybe me!). You can also contact me directly at zack dot scholl at gmail dot com.</p>
]]></description>
    </item>
    
    <item>
	    <title>Endure</title>
	    <image>https://schollz.com</image>
      <link>/books/endure/</link>
      <pubDate>Mon, 11 Mar 2019 16:29:18 -0700</pubDate>
      
      <guid>/books/endure/</guid>
      <description><![CDATA[<p>This book, &ldquo;Endure: Mind, Body, and the Curiously Elastic Limits of Human Performance&rdquo; contains a lot of interesting insights and technical aspects of studying human endurance. I immensely liked the Lore of Running by Tim Noakes, and this book is a deeper dive into a lot of specific studies into the limits to endurance. I liked reading about the different experiments but couldn&rsquo;t help think that they may have been slightly cherry-picked as there is a lot of focus on a group of experiments. It was also harder to read as it is somewhat clinical in a lot of chapters (though at certain times I do appreciate that).</p>
]]></description>
    </item>
    
    <item>
	    <title>FAAS</title>
	    <image>https://schollz.com/img/faas.png</image>
      <link>/tinker/faas/</link>
      <pubDate>Mon, 11 Mar 2019 11:54:49 -0700</pubDate>
      
      <guid>/tinker/faas/</guid>
      <description><![CDATA[<p>This is a FaaS: <em>functions as a service</em>. But, in actuality, its more of a FaaSSS: <em>functions as a stupidly simple service</em>. Imagine <a href="https://github.com/iron-io/functions" target="_blank" >iron.io/functions</a>, or <a href="https://github.com/zeit/now-cli" target="_blank" >zeit/now</a> or <a href="https://github.com/openfaas/faas" target="_blank" >openfaas</a> or <a href="https://github.com/apex/apex" target="_blank" >apex</a> or <a href="https://github.com/briandowns/sky-island" target="_blank" >sky-island</a> but more simple and more stupid.</p>
<p>Unlike others, this FaaS requires <strong>no coding</strong>, <strong>no init-ing</strong>, <strong>no pushing</strong>, <strong>no updating</strong>, and <strong>no bumping</strong>. You Just make <em>one</em> HTTP request with the name of the package, the name of the function, and any input. Right now it only works for Go. It can easily be extended to Python (WIP).</p>
<p>The codebase is only about 600 lines of code in total. Basically a request tells the server to fetch the function, determine the inputs/outputs and write a wrapper function that is served from a Docker container.</p>
<h2 id="examples">Examples</h2>
<p>Try it right now with my instance at <a href="https://faas.schollz.com" target="_blank" >https://faas.schollz.com</a>.</p>
<p>You can make (almost) any exported Go function into a API!</p>
<p>Run <a href="https://github.com/schollz/utils/blob/adaa47085f7b6b1c3e1ecfebfb18028e08e0bde2/hash.go#L29-L34" target="_blank" ><code>Md5Sum</code></a> to get a md5 hash of &ldquo;hello, world&rdquo;:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl">$ curl https://faas.schollz.com/?import<span class="o">=</span>github.com/schollz/utils<span class="p">&amp;</span><span class="nv">func</span><span class="o">=</span>Md5Sum<span class="o">(</span>%22hello,%20world%22<span class="o">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl">e4d7f1b4ed2e42d15898f4b27b019da4
</span></span></code></pre></div><p>Run <a href="https://github.com/schollz/ingredients/blob/23a2a0c2d9dc8988c33acf7650ae9284a59d0b20/ingredients.go#L153-L160" target="_blank" ><code>IngredientsFromURL</code></a> to get the ingredients from any website:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">$ curl https://faas.schollz.com/?import<span class="o">=</span>github.com/schollz/ingredients<span class="p">&amp;</span><span class="nv">func</span><span class="o">=</span>IngredientsFromURL<span class="o">(</span>%22https://cooking.nytimes.com/recipes/12320-apple-pie%22<span class="o">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl"><span class="o">{</span><span class="s2">&#34;ingredients&#34;</span>: <span class="o">[{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;butter&#34;</span>,<span class="s2">&#34;comment&#34;</span>:<span class="s2">&#34;unsalted&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:2,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;tablespoons&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.25<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;apples&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:2.5,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;pounds&#34;</span>,<span class="s2">&#34;cups&#34;</span>:14.901182654402104<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;allspice&#34;</span>,<span class="s2">&#34;comment&#34;</span>:<span class="s2">&#34;ground&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:0.25,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;teaspoon&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.0104165<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;cinnamon&#34;</span>,<span class="s2">&#34;comment&#34;</span>:<span class="s2">&#34;ground&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:0.5,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;teaspoon&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.020833<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;salt&#34;</span>,<span class="s2">&#34;comment&#34;</span>:<span class="s2">&#34;kosher&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:0.25,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;teaspoon&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.0104165<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;sugar&#34;</span>,<span class="s2">&#34;comment&#34;</span>:<span class="s2">&#34;plus 1 tablespoon&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:0.75,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;cup&#34;</span>,<span class="s2">&#34;cups&#34;</span>:1.5<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;flour&#34;</span>,<span class="s2">&#34;comment&#34;</span>:<span class="s2">&#34;all purpose&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:2,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;tablespoons&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.25<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;cornstarch&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:2,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;teaspoons&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.083332<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;apple cider vinegar&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:1,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;tablespoon&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0.125<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;pie dough&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:1,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;whole&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0<span class="o">}}</span>,<span class="o">{</span><span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;egg&#34;</span>,<span class="s2">&#34;measure&#34;</span>:<span class="o">{</span><span class="s2">&#34;amount&#34;</span>:1,<span class="s2">&#34;name&#34;</span>:<span class="s2">&#34;whole&#34;</span>,<span class="s2">&#34;cups&#34;</span>:0<span class="o">}}]</span>,<span class="s2">&#34;err&#34;</span>: null
</span></span></code></pre></div><p>Run <code>MarkdownToHTML</code> from a <a href="https://play.golang.org/p/9xzE8Ivwupk" target="_blank" >Go playground</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl">$ curl -d <span class="s1">&#39;{&#34;markdown&#34;:&#34;*hello*,**world**&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a></span><span class="cl"><span class="se"></span> 	-H <span class="s2">&#34;Content-Type: application/json&#34;</span> -X POST <span class="se">\
</span></span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3">3</a></span><span class="cl"><span class="se"></span> 	https://faas.schollz.com/?import<span class="o">=</span>https://play.golang.org/p/9xzE8Ivwupk.go<span class="p">&amp;</span><span class="nv">func</span><span class="o">=</span>MarkdownToHTML
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4">4</a></span><span class="cl">&lt;p&gt;&lt;em&gt;hello&lt;/em&gt;,&lt;strong&gt;world&lt;/strong&gt;&lt;/p&gt;
</span></span></code></pre></div><h2 id="usage">Usage</h2>
<p>You can use <code>GET</code> or <code>POST</code> to submit jobs.</p>
<p>For the <code>GET</code> requests the syntax is</p>
<pre tabindex="0"><code>/?import=IMPORTPATH&amp;func=FUNCNAME(param1,param2...)
</code></pre><p>The <code>IMPORTPATH</code> is the import path (e.g. github.com/x/y) or a URL containing the file with the function. The <code>FUNCNAME</code> is the name of the function. Note, you do need to URL encode the strings so that <code>FUNCNAME(&quot;hello, world&quot;) -&gt; FUNCNAME(%22hello,%20world%22)</code></p>
<p>For the <code>POST</code> requests the syntax is:</p>
<pre tabindex="0"><code>/?import=IMPORTPATH&amp;func=FUNCNAME
</code></pre><p>with the body with the inputs <code>{&quot;param&quot;:&quot;value&quot;}</code>.</p>
<p>That&rsquo;s it! The first time you run it will take ~1 minute while the Docker image is built.</p>
<h2 id="how-does-it-work">How does it work?</h2>
<p>When you make a <code>POST</code>/<code>GET</code> request to the <code>faas</code> server it will locate the given function and given package and it will generate a Docker container that accepts a JSON input with the parameters and outputs a JSON containing the output variables. This Docker container automatically shuts down after some time of inactivity. Subsequent requests then will load the previously built container for use.</p>
<h2 id="host-yourself">Host yourself</h2>
<p>You need to <a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-docker-engine---community-1" target="_blank" >install Docker</a>, and make sure <code>gzip</code> is installed.</p>
<p>Then build <code>faas</code> with Go:</p>
<pre tabindex="0"><code>git clone https://github.com/schollz/faas
cd faas
go generate
go build -v
</code></pre><p>Now you can run:</p>
<pre tabindex="0"><code>./faas --debug
</code></pre><p>Now you can try it out:</p>
<pre tabindex="0"><code>curl http://localhost:8090/?import=github.com/schollz/utils&amp;func=Md5Sum(%22hello,%20world%22)
</code></pre><p>OR post data:</p>
<pre tabindex="0"><code>curl -d &#39;{&#34;s&#34;:&#34;hello, world&#34;}&#39; -H &#34;Content-Type: application/json&#34; -X POST http://localhost:8090/?import=github.com/schollz/utils&amp;func=Md5Sum
</code></pre><p>Note that the JSON <code>&quot;s&quot;</code> comes from the function <code>Md5Sum</code> itself.</p>
]]></description>
    </item>
    
    <item>
	    <title>What Doesn&#39;t Kill Us</title>
	    <image>https://schollz.com</image>
      <link>/books/whatdoesnt/</link>
      <pubDate>Fri, 01 Feb 2019 16:30:50 -0700</pubDate>
      
      <guid>/books/whatdoesnt/</guid>
      <description><![CDATA[<p>This book fully titled &ldquo;What Doesn&rsquo;t Kill Us: How Freezing Water, Extreme Altitude, and Environmental Conditioning Will Renew Our Lost Evolutionary Strength &quot; follows the skeptical journalist&rsquo;s take on a man who believes he can give anyone the power to endure extremely cold temperatures.</p>
<p>I like the gonzo-like journalism but it makes the claims hard to believe, especially when the ice training the journalist undergoes maybe just helps him climb a mountain because he is no longer out of shape.</p>
<p>Anyways, the first part is the story of Wim Hof, the &ldquo;Ice man&rdquo;, who has amazing feats of endurance - physical and mental - which can&rsquo;t be disregarded easily. Whether he can teach these techniques to me is still debatable.</p>
]]></description>
    </item>
    
    <item>
	    <title>Shadow Divers</title>
	    <image>https://schollz.com</image>
      <link>/books/divers/</link>
      <pubDate>Fri, 25 Jan 2019 16:29:01 -0700</pubDate>
      
      <guid>/books/divers/</guid>
      <description><![CDATA[<p>The full title (for SEO) is &ldquo;Shadow Divers: The True Adventure of Two Americans Who Risked Everything to Solve One of the Last Mysteries of World War II&rdquo;. Before reading this I did not have any interest in diving or underwater exploratoin. This book is written so well I could not believe it was nonfiction. The story of these dives to discover German U-boats its absoultely engrossing and I emerged from this book with a profound admiration for these divers and their accomplishments and a respect for underwater exploration in general.</p>
]]></description>
    </item>
    
    <item>
	    <title>Shoeless Joe</title>
	    <image>https://schollz.com</image>
      <link>/books/joe/</link>
      <pubDate>Tue, 22 Jan 2019 16:28:34 -0700</pubDate>
      
      <guid>/books/joe/</guid>
      <description><![CDATA[<p>I got this book after hearing <a href="https://www.thisamericanlife.org/664/transcript" target="_blank" >Episode #664</a> of the This American Life podcast. In the podcast one of the people meets Kinsella, the author and they share a reverence for Richard Brautigan and his Brautigan library and weird fiction. Anyways, this is a weird book, sort of magical realism for the corn fields of Iowa. It is a great read and its interesting to watch the movie because it very closely adapts the book (something which is not as common to see anymore).</p>
]]></description>
    </item>
    
    <item>
	    <title>Download songs from a Spotify playlist</title>
	    <image>https://schollz.com/img/sdown.gif</image>
      <link>/tinker/spotify/</link>
      <pubDate>Fri, 11 Jan 2019 07:02:12 -0700</pubDate>
      
      <guid>/tinker/spotify/</guid>
      <description><![CDATA[<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.7.1/gist-embed.min.js"></script>
<p>I listen to a lot of Spotify and make a lot of playlists. I&rsquo;d like to listen to them offline, for personal use only - so I could put them on my old-fashioned mp3 player. I decided to try out a couple of &ldquo;Spotify downloaders&rdquo; but in the end I decided to make my own.</p>
<p><strong>Disclaimer:</strong> Downloading copyright songs may be illegal in your country. This tool is for educational purposes only and was created only to show how Spotify&rsquo;s API can be used to download music from YouTube. Please support the artists by buying their music.</p>
<p>A search for &ldquo;Spotify Download&rdquo; gives about <a href="https://github.com/search?q=spotify&#43;download" target="_blank" >250 results</a>, some which are quiet popular. I tried the two most popular - one written in Python and one written in Node. Both of them resulted in errors despite my system being new and despite having all of my dependencies up-to-date. Maybe it&rsquo;s a Windows thing?</p>
<p>First I tried a Spotify downloader written in Python, <a href="https://github.com/ritiek/spotify-downloader" target="_blank" ><code>spotdl</code></a> (run using Python 3.7.1 on Windows 10). It resulted in over <a href="https://gist.github.com/schollz/5f7b8ebcb2fbf3b1fcd7f2d45030ab64/#file-spotdl-txt" target="_blank" >300 lines of errors</a>. Something about <code>Could not find JS function 'encodeURIComponent'</code>. It turned out to be a <code>youtube-dl</code> problem, which I then upgraded to the new current version. I tried it again and immediately had problems with correct detection of songs. For instance a search for <em>Ian William Craig - Arrive Arrive</em> resolted in a video <a href="http://www.youtube.com/watch?v=0P0h3JmS3gw" target="_blank" >parodying Hitler</a>.</p>
<p>But eventually I would also get a <code>403</code> error and then another <code>NameError: name 'time' is not defined</code> error. Turns out this was a bug fix to another bug fix <a href="https://github.com/ritiek/spotify-downloader/issues/464" target="_blank" >that wasn&rsquo;t pushed to PyPI yet</a>. Ugh Python ecosystems.</p>
<p>Then, I tried a different Spotify downloader written in Node, <a href="https://github.com/qrpike/spotify-playlist-downloader" target="_blank" ><code>spotify-playlist-downloader</code></a> (run using Node 10.14.1 on Windows 10) which also resulted in an error immediately, something about <a href="https://gist.github.com/schollz/5f7b8ebcb2fbf3b1fcd7f2d45030ab64/#file-spotify-playlist-downloader-txt" target="_blank" ><code>Cannot read property 'csrftoken' of undefined</code></a>.</p>
<h2 id="writing-my-own-spotifydownload">Writing my own: spotifydownload</h2>
<p>In the end, I simply wrote another. I decided to write it in Go, because there would be no dependenecies and anyone could run it without installing an ecosystem (e.g. Python and Node).</p>
<p>There are basically two components to this program: a module for getting songs (<a href="https://github.com/schollz/getsong" target="_blank" >getsong</a>) and a command-line utility for downloading the playlists (<a href="https://github.com/schollz/spotifydownload" target="_blank" >spotifydownload</a>).</p>
<p>Getting the songs of YouTube is an important and tricky part. As I saw above, trying to get the song <em>Arrive, Arrive by Ian William Craig</em> resulted in downloading an audio clip from a Hitler parody. The song finding has to have a very high positive rate and a low false positive rate.</p>
<p>To get the songs then, first I have to make sure that the title of the song and the name of the artist are <em>spelled right</em>. Spelling is important, because YouTube will try to change the spelling if they think it is spelled wrong. For example, when searching for the song <em>Eva by Haerts</em>, the name of the band <em>Haerts</em> seems to be mispelled even though it it isn&rsquo;t. A search for this song shows YouTube automatically corrects it:</p>
<p><img src="/img/search-instead.jpg" alt="YouTube incorrectly correcting &ldquo;Haerts&rdquo;"></p>
<p>In this situation, you would actually need to load the page from the &ldquo;Search instead for <em>eva haerts</em>&rdquo;. Following this link gives the correct results as you would have expected.</p>
<p>Once possible videos are found, then you have to find the <em>best</em> one which best matches the song, as to avoid Hitler parodies or other random videos. A good way I found to do this was to look for auto-generated videos where have the keywords &ldquo;auto-generated&rdquo; or &ldquo;Provided to YouTube&rdquo;. This avoids getting songs that are simply &ldquo;live&rdquo; versions of other songs.</p>
<p><img src="/img/provided-to-youtube.jpg" alt="Music videos provided to YouTube"></p>
<p>Another important aspect is to skip getting songs that are unable to find the right video. The right video is defined strictly - it is a video that <em>must</em> have the name of the artist in it somewhere, and it <em>must</em> have the title of the song somewhere.</p>
<p>The end result of this is <a href="https://github.com/schollz/getsong" target="_blank" >github.com/schollz/getsong</a>. There is a command-line version of this you can run with</p>
<pre tabindex="0"><code>$ go get github.com/schollz/getsong
</code></pre><p>You can use this to download individual songs. For instance, if you want to download <em>Arrive, Arrive</em> it will easily find the <a href="https://www.youtube.com/watch?v=_GYMVk7Bu8o" target="_blank" >right YouTube video to download</a> instead of a Hitler parody.</p>
<pre tabindex="0"><code>$ getsong &#39;Arrive, Arrive&#39; &#39;Ian William Craig&#39;
Downloading Ian William Craig - Arrive, Arrive (_GYMVk7Bu8o)...
 524289 / 524289 [=================================] 100.00% 668149/s 0s
...converting to mp3...
Downloaded &#39;Ian William Craig - Arrive, Arrive (_GYMVk7Bu8o).mp3&#39;
</code></pre><h3 id="getting-playlists-schollzspotifydownload">Getting playlists (schollz/spotifydownload)</h3>
<p>The next part is to get the playlists from Spotify and then download all of the songs in the playlist. This is actually very easy as Spotify provides a link to the playlist. Simply go to a playlist and right-click and you&rsquo;ll see a dropdown menu that lists &ldquo;Share Playlist link&rdquo; as an option. Parsing this web link will give you a list of songs and artists that can then be downloaded one at a time using <a href="https://github.com/schollz/getsong" target="_blank" >getsong</a>.</p>
<p>I tried doing this other ways, like using a Bearer key, but that would only be useful for private playlists.</p>
]]></description>
    </item>
    
    <item>
	    <title>Books I read (2018)</title>
	    <image>https://schollz.com/img/books2018.jpg</image>
      <link>/tinker/books-2018/</link>
      <pubDate>Sat, 15 Dec 2018 09:41:01 -0600</pubDate>
      
      <guid>/tinker/books-2018/</guid>
      <description><![CDATA[<p><a href="/blog/books-2017/" ><em>(click here to see the books I enjoyed last year)</em></a></p>
<h2 id="finding-books">Finding books</h2>
<p>To find good books this year, I <a href="https://www.booksuggestions.ninja/" target="_blank" >re-wrote my book suggestions webapp</a> and tried to find a book similar to <strong>Imaginary Magnitude</strong> by <em>Stanisław Lem</em>. The book that it found is my favorite book of 2018: <strong>Stories of your Life and Others</strong> by <em>Ted Chiang</em>. It is a brilliant ode to physics with great storytelling with characters that are both feeling and emotional as well as rational.</p>
<h2 id="best-fiction">Best fiction</h2>
<p>This is the first year I kept a list of books I wanted to read. It helped me to stay prioritized about what I read. Early on I read some really good fictional books. I was looking forward to <strong>Artemis</strong> by <em>Andy Weir</em> and it did not disappoint - though certainly different than <strong>The Martian</strong>.</p>
<p>I found a lot of new fiction to read this last year - I got hooked onto Ottessa Moshfegh (<strong>Homesick for Another World</strong>, <strong>My Year of Rest and Relaxation</strong>, <strong>Eileen</strong>) who is like a modern day Charles Bukowski, and I enjoyed reading Miranda July (<strong>The First Bad Man</strong>, <strong>No one belongs here more than you</strong>) who writes about strange out-of-the-box people.</p>
<p>A good friend recommended some great coming-of-age books: <strong>Lily</strong> by <em>Michael Ford</em>, a book about a magic witch and another world (slightly grim), and <strong>The Highest Tide</strong> by <em>Jim Lynch</em>, a book about a world who catapults a boy because of his supposed connection with ocean animals.</p>
<h2 id="best-nonfiction">Best nonfiction</h2>
<p>I also found some great non-fiction reads. <em>Michael Pollan</em>&rsquo;s <strong>How to Change Your Mind</strong> led me to a rabbit-hole of looking at papers on psychoactive drugs which I found fascinating because of their significant and strong effects as an alternative medicine.</p>
<p>I learned a lot from <strong>Salt, Fat, Acid, Heat</strong> by <em>Samin Nosrat</em> and because of that book I can easily cook chicken with perfection (pre-salting it in the fridge). I started, but didn&rsquo;t finish <strong>Endure</strong> by <em>Alex Hutchinson</em> which goes over some interesting scientific experiments and anecdotes about what it means to have endurance and what limits are known to science.</p>
<p>Other books I read: <strong>Hackers &amp; Painters</strong> by <em>Paul Graham</em>, <strong>Sandman Slim</strong> by <em>Richard Kadrey</em>,  <strong>Bad Luck and Trouble</strong> by <em>Lee Child</em>, and <strong>Ask Me About Polyamory</strong> by <em>Tikva Wolf</em>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Photos I took</title>
	    <image>https://schollz.com/img/2018/canada2.jpg</image>
      <link>/tinker/photos-2018/</link>
      <pubDate>Sat, 15 Dec 2018 09:41:01 -0600</pubDate>
      
      <guid>/tinker/photos-2018/</guid>
      <description><![CDATA[<p>These photos really narrate pieces of my life from 2018. For instance, I got a cat.</p>
<p><img src="/img/2018/cat.jpg" alt="My cat."></p>
<p>I went for a walk in the Seattle Arboretum park during the spring.</p>
<p><img src="/img/2018/park.jpg" alt="Seattle in the Spring."></p>
<p>I walked in a park in Hillsboro that commissioned some neat stick structures. There are many of these throughout the world, they are made by a community with the expert assistance of <a href="http://www.stickwork.net/" target="_blank" >Patrick Dougherty</a> who has been making these types of structures for over 30 years.</p>
<p><img src="/img/2018/faces.jpg" alt="Stick faces"></p>
<p>I watched the very naked bike ride in Seattle. Do you know where your Lime bike has been?</p>
<p><img src="/img/2018/seattle.jpg" alt="End of the ride"></p>
<p>I took a picture of a couple at the top of Mt. Pilchuck when I visited there in early October.</p>
<p><img src="/img/2018/view.jpg" alt="Top of Mt. Pilchuck"></p>
<p>There was great views at Heather Lake, lots of impressive rocks.</p>
<p><img src="/img/2018/rock.jpg" alt="Heather Lake"></p>
<p>On our way to the Mt. Baker hot springs I stopped off at Swift creek with a makeshift bridge and a very green mossy forest.</p>
<p><img src="/img/2018/bridge.jpg" alt="A makeshift bridge"></p>
<p><img src="/img/2018/forest.jpg" alt="A very mossy green forest"></p>
<p>I traveled through Jasper park in Canada and took some neat photos.</p>
<p><img src="/img/2018/canada.jpg" alt="Rocks and mountains in Jasper"></p>
<p><img src="/img/2018/canada2.jpg" alt="A view from far away"></p>
]]></description>
    </item>
    
    <item>
	    <title>Podcasts I heard</title>
	    <image>https://schollz.com/img/podcasts.jpg</image>
      <link>/tinker/podcasts-2018/</link>
      <pubDate>Sat, 15 Dec 2018 09:41:01 -0600</pubDate>
      
      <guid>/tinker/podcasts-2018/</guid>
      <description><![CDATA[<p><a href="/other/podcasts-2017/" ><em>(click here to see the podcasts I enjoyed last year)</em></a></p>
<p>There is a new fad in podcasting where you make a short, digestible series with a finite set of episodes from the beginning. Its something like <strong>S-town</strong>, but it can be fiction or non-fiction. There were two great podcasts like that this year. The first is <a href="https://www.npr.org/podcasts/606441988/bundyville" target="_blank" ><strong>Bundyville</strong></a> by NPR, which chronicles the beginning of the Bundy family and how it led to the occupation of the Oregon bunker in 2016. Another incredible one is <a href="https://www.gimletmedia.com/the-habitat" target="_blank" ><strong>The Habitat</strong></a>, which tells the true story of six people who chose to live on a simulated &ldquo;Mars&rdquo; to test the viability of human interactions in a closed setting.</p>
<p>Another great idea podcast is <a href="https://www.vox.com/the-impact" target="_blank" ><strong>The Impact</strong></a> which explores the consequences that laws have on real people. Its a great podcast as it treats cities and states like mini experiments in public policy and it does a good job of demonstrating the observations.</p>
<p>I also enjoyed some more personal podcasts - <a href="https://www.gimletmedia.com/reply-all" target="_blank" ><strong>Reply All</strong></a> from Gimlet has a lot of original stories about interesting people. My favorite new funny podcast is <a href="https://headgum.com/why-wont-you-date-me" target="_blank" ><strong>Why Won’t You Date Me</strong></a> which is a deeply personal account of a very funny woman, <em>Nicole Byer</em>.</p>
<p>A lot of the podcasts I listened to <a href="/other/podcasts-2017/" >last year</a> are still great, and still listen to.</p>
]]></description>
    </item>
    
    <item>
	    <title>Achievement</title>
	    <image>https://schollz.com/img/newyorker/achievement.jpg</image>
      <link>/newyorker/achievement/</link>
      <pubDate>Sun, 11 Nov 2018 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/achievement/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 11/11/2018.</p>
<p>Declined by The New Yorker on 3/1/2019.</p>
<p><img src="/img/newyorker/achievement.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Corn maze</title>
	    <image>https://schollz.com/img/newyorker/cornmaze.jpg</image>
      <link>/newyorker/cornmaze/</link>
      <pubDate>Sun, 11 Nov 2018 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/cornmaze/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 11/11/2018.</p>
<p>Declined by The New Yorker on 3/1/2019.</p>
<p><img src="/img/newyorker/cornmaze.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Crocheting</title>
	    <image>https://schollz.com/img/newyorker/crochet.jpg</image>
      <link>/newyorker/crochet/</link>
      <pubDate>Sun, 11 Nov 2018 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/crochet/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 11/11/2018.</p>
<p>Declined by The New Yorker on 3/1/2019.</p>
<p><img src="/img/newyorker/crochet.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>HOV lane</title>
	    <image>https://schollz.com/img/newyorker/hov.jpg</image>
      <link>/newyorker/hov/</link>
      <pubDate>Sun, 11 Nov 2018 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/hov/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 11/11/2018.</p>
<p>Declined by The New Yorker on 3/1/2019.</p>
<p><img src="/img/newyorker/hov.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>My other bike</title>
	    <image>https://schollz.com/img/newyorker/othercarisabike.jpg</image>
      <link>/newyorker/bike/</link>
      <pubDate>Sun, 11 Nov 2018 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/bike/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 11/11/2018.</p>
<p>Declined by The New Yorker on 3/1/2019.</p>
<p><img src="/img/newyorker/othercarisabike.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>Neti pot</title>
	    <image>https://schollz.com/img/newyorker/neti.jpg</image>
      <link>/newyorker/neti/</link>
      <pubDate>Sun, 11 Nov 2018 10:01:55 -0700</pubDate>
      
      <guid>/newyorker/neti/</guid>
      <description><![CDATA[<p>Submitted to The New Yorker on 11/11/2018.</p>
<p>Declined by The New Yorker on 3/1/2019.</p>
<p><img src="/img/newyorker/neti.jpg" alt=""></p>
]]></description>
    </item>
    
    <item>
	    <title>A website for jotting notes.</title>
	    <image>https://schollz.com/img/projects/rwtxt.jpg</image>
      <link>/tinker/rwtxt/</link>
      <pubDate>Fri, 12 Oct 2018 07:50:07 -0700</pubDate>
      
      <guid>/tinker/rwtxt/</guid>
      <description><![CDATA[<p>TLDR: Use the website at <a href="https://rwtxt.com" target="_blank" >rwtxt.com</a>.</p>
<p><em>rwtxt</em> is an <a href="https://github.com/schollz/rwtxt" target="_blank" >open-source</a> website where you can store any text online for easy sharing and quick recall. In more specific terms, it is a light-weight and fast content management system (CMS) where you write in Markdown with emphasis on reading.</p>
<p><em>rwtxt</em> builds off <a href="https://cowyo.com" target="_blank" >cowyo</a>, a similar app I made previously. In improving with <em>rwtxt</em> I aimed to avoid <a href="https://en.wikipedia.org/wiki/Second-system_effect" target="_blank" >second-system syndrome</a>: I got rid of features I never used in cowyo (self-destruction, encryption, locking), while integrating a useful new feature not available previously: you can create  <em>domains</em>. A <em>domain</em> is basically a personalized namespace where you can write private/public posts that are searchable. I personally use <em>rwtxt</em> to collect and jot notes for work, personal, coding - each which has its own searchable and indexed domain.</p>
<p><em>rwtxt</em> is backed by a single <a href="https://www.sqlite.org/index.html" target="_blank" >sqlite3</a> database, so it&rsquo;s portable and very easy to backup. It&rsquo;s written in Go and all the assets are bundled so you can just download a single binary and start writing. You can also try the online version: <a href="https://rwtxt.com/public" target="_blank" >rwtxt.com</a>.</p>
<h2 id="usage">Usage</h2>
<p><strong>Reading.</strong> You can share <em>rwtxt</em> links to read them on another computer. <em>rwtxt</em> is organized in <em>domains</em> - places where you can privately or publicly store your text. If the <em>domain</em> is private, you must be signed in to read, even you have the permalink.</p>
<p>You can easily <a href="https://rwtxt.com/public" target="_blank" >create your own domain</a> in 10 seconds. When you make a new domain it will be private by default, so only you can view/search/edit your own text.</p>
<p>Once you make a domain you will see an option to make your domain <em>public</em> so that anyone can view/search it. However, only people with the domain password can edit in your domain - making <em>rwtxt</em> useful as a password-protected wiki. (The one exception is the <a href="https://rwtxt.com/public" target="_blank" ><code>/public</code></a> domain, which anyone can edit/view - making <em>rwtxt</em> useful as a pastebin).</p>
<p><strong>Writing.</strong> To write in <em>rwtxt</em>, just create a new page and click &ldquo;Edit&rdquo;, or goto a URL for the thing you want to write about - like <code>rwtxt.com/something-i-want-to-write</code>. When you write in <em>rwtxt</em> you can format your text in <a href="https://guides.github.com/features/mastering-markdown/" target="_blank" >Markdown</a>.</p>
<p>In addition, writing triple backtick code blocks:</p>
<pre><code>```javascript
console.log(&quot;hello, world&quot;);
```
</code></pre>
<p>produces code highlighted with <a href="https://prismjs.com/" target="_blank" >prism.js</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;hello, world&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p><strong>Deleting.</strong> You can easily delete your page. Just erase all the content from it and it will disappear forever within 10 seconds.</p>
<h2 id="install">Install</h2>
<p>You can easily install and run <em>rwtxt</em> on your own computer.</p>
<p>You can download a binary for the <a href="https://github.com/schollz/rwtxt/releases/latest" target="_blank" >latest release</a>.</p>
<p>Or you can install from source. First, make sure to <a href="https://golang.org/dl/" target="_blank" >install Go</a>. Then clone the repo:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">$ git clone https://github.com/schollz/rwtxt.git
</span></span></code></pre></div><p>Then you can make it with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl">$ make
</span></span></code></pre></div><p>And then run it!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a></span><span class="cl">$ <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">PATH</span><span class="si">}</span><span class="s2">:</span><span class="si">${</span><span class="nv">GOPATH</span><span class="si">}</span><span class="s2">/bin&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a></span><span class="cl">$ rwtxt
</span></span></code></pre></div><h3 id="docker">Docker</h3>
<p>You can also easily install and run with Docker.</p>
<pre tabindex="0"><code>$ docker pull schollz/rwtxt
</code></pre><p>Then run by using docker</p>
<pre tabindex="0"><code>$ docker run -v /place/to/store/data:/data -p 8000:8152 schollz/rwtxt
</code></pre><p>In this case <code>-p 8000:8152</code> will have rwtxt will be running on port 8000.</p>
]]></description>
    </item>
    
    <item>
	    <title>About my art</title>
	    <image>https://schollz.com</image>
      <link>/painting/about/</link>
      <pubDate>Wed, 10 Oct 2018 07:44:51 -0700</pubDate>
      
      <guid>/painting/about/</guid>
      <description><![CDATA[<p>I started painting large animals as a way to observe life on a scale that is no longer microscopic. Animals are a
million times bigger than the usual objects of my observation, but that makes them no less fascinating and
intriguing. Painting provides a totally new lens to look at the world around me, it allows me to truly see the
colors and the shapes on a way I’ve never appreciated at a scale I’ve often neglected to observe.</p>
<p>I like my paintings to deliver the sense of scale and magnificence of these creatures, so I often try to make
close-to one-to-one representations. I also enjoy revealing the process of painting itself through my paintings,
which is why many of the brush strokes are more obvious. Brush strokes also give a sense of motion to the painting,
which is fitting since many of our interactions with these animals is fleeting.</p>
]]></description>
    </item>
    
    <item>
	    <title>🐊 croc - secure and easy data transfer</title>
	    <image>https://schollz.com/img/46709024-9b23ad00-cbf6-11e8-9fb2-ca8b20b7dbec.jpg</image>
      <link>/tinker/croc/</link>
      <pubDate>Wed, 10 Oct 2018 07:02:12 -0700</pubDate>
      
      <guid>/tinker/croc/</guid>
      <description><![CDATA[<p>There are many tools that can do this but AFAIK <em>croc</em> is the only tool that is easily installed and used on any platform, <em>and</em> has secure peer-to-peer transferring, <em>and</em> has the capability to resume broken transfers.</p>
<h2 id="overview">Overview</h2>
<p><strong>Transmit encrypted data with a code phrase</strong></p>
<p><em>croc</em> securely transfers data using <em>code phrases</em> - a combination of three random words (mnemonicoded 4 bytes). The code phrase is shared between the sender and the recipient for password authenticated key exchange (<a href="https://github.com/schollz/pake" target="_blank" >PAKE</a>), a cryptographic method to use a shared weak key (the &ldquo;code phrase&rdquo;) to generate a strong key for secure end-to-end encryption. By default, a code phrase can only be used once between two parties so an attacker would have a chance of less than 1 in <em>4 billion</em> to guess the code phrase correctly to steal the data. An attacker with the wrong code phrase will fail the PAKE and the sender will be notified without any data transfering. Only two people with the right code phrase will be able to computers transfer encrypted data through a relay.</p>
<p><strong>Fast data transfer through TCP</strong></p>
<p>The actual data transfer is accomplished using a relay, either using raw TCP sockets or websockets. If both computers are on the LAN network then <em>croc</em> will use a local relay, otherwise a public relay is used. All the data going through the relay is encrypted using the PAKE-generated session key, so the relay can&rsquo;t spy on information passing through it. The data is transferred in blocks, where each block is compressed and encrypted, and the recipient keeps track of blocks received so that it can resume the transfer if interrupted.</p>
<p><strong>Relay allows any two computers to connect</strong></p>
<p><em>croc</em> differs from a utility like <em>scp</em> because it doesn&rsquo;t require any two computers to have enabled port-forwarding. Instead, <em>croc</em> will uses a relay - a temporary server setup locally (if both computers are on lan) or publicly (default is at croc4.schollz.com). Any two computers can connect to the relay, and after securing their channel with PAKE, they can transfer encrypted metadata and data through the relay. The relay works by first having the computers communicate the PAKE protocol via websockets, and then exchanging encrypted metadata, and then stapling the TCP connections directly so that they can transfer directly.</p>
<p><strong>Why another data transfer utility?</strong></p>
<p>My motivation to write <em>croc</em>, as stupid as it sounds, is because I wanted to create a program that made it easy to send a 3GB+ PBS documentary to my friend in a different country. My friend has a Windows computer and is not comfortable using a terminal. So I wanted to write a program that, while secure, is simple to receive a file. <em>croc</em> accomplishes this, and now I find myself using it almost everyday at work. To receive a file you can just download the executable and double click on it. The name is inspired by the <a href="https://web.archive.org/web/20180926035731/http://allaboutfrogs.org/stories/crocodile.html" target="_blank" >fable of the frog and the crocodile</a>.</p>
<h2 id="examples">Examples</h2>
<p>The first example shows the basic transfer of some file or folder from computer 1 to computer 2. <em>These two gifs should run in sync if you force-reload (Ctl+F5)</em></p>
<p><img src="https://raw.githubusercontent.com/schollz/croc/master/.github/1.gif" alt="Sender"></p>
<p><img src="https://raw.githubusercontent.com/schollz/croc/master/.github/2.gif" alt="Receiver"></p>
<p>The second example shows how you can restart a broken transfer. Here, computer 2 presses Ctl+C during a transfer to abruptly break the connection, and then resumes by having computer 1 re-send the file. <em>These two gifs should run in sync if you force-reload (Ctl+F5)</em></p>
<p><img src="https://raw.githubusercontent.com/schollz/croc/master/.github/3.gif" alt="Sender"></p>
<p><img src="https://raw.githubusercontent.com/schollz/croc/master/.github/4.gif" alt="Receiver"></p>
<h2 id="install">Install</h2>
<p><a href="https://github.com/schollz/croc/releases/latest" target="_blank" >Download the latest release for your system</a>.</p>
<p>Or, you can <a href="https://golang.org/dl/" target="_blank" >install Go</a> and build from source with <code>go get github.com/schollz/croc</code>.</p>
<p>Or, you can quickly install a release from the command-line:</p>
<pre tabindex="0"><code>$ curl https://getcroc.schollz.com | bash
</code></pre><pre tabindex="0"><code>$ wget -qO- https://getcroc.schollz.com | bash
</code></pre><h2 id="usage">Usage</h2>
<p>The basic usage is to just do</p>
<pre tabindex="0"><code>$ croc send FILE
</code></pre><p>to send and then on the other computer you can just do</p>
<pre tabindex="0"><code>$ croc [code phrase]
</code></pre><p>to receive (you&rsquo;ll be prompted to enter the code phrase). Note, by default, you don&rsquo;t need any arguments for receiving, instead you will be prompted to enter the code phrase. This makes it possible for you to just double click the executable to run (nice for those of us that aren&rsquo;t computer wizards).</p>
<h3 id="custom-code-phrase">Custom code phrase</h3>
<p>You can send with your own code phrase (must be more than 4 characters).</p>
<pre tabindex="0"><code>$ croc send --code [code phrase] [filename]
</code></pre><h3 id="use-locally">Use locally</h3>
<p><em>croc</em> automatically will attempt to start a local connection on your LAN to transfer the file much faster. It uses <a href="https://github.com/schollz/peerdiscovery" target="_blank" >peer discovery</a>, basically broadcasting a message on the local subnet to see if another <em>croc</em> user wants to receive the file. <em>croc</em> will utilize the first incoming connection from either the local network or the public relay and follow through with PAKE.</p>
<p>You can change this behavior by forcing <em>croc</em> to use only local connections (<code>--local</code>) or force to use the public relay only (<code>--no-local</code>):</p>
<pre tabindex="0"><code>$ croc --local/--no-local send [filename]
</code></pre><h3 id="using-pipes---stdin-and-stdout">Using pipes - stdin and stdout</h3>
<p>You can easily use <em>croc</em> in pipes when you need to send data through stdin or get data from stdout. To send you can just use pipes:</p>
<pre tabindex="0"><code>$ cat [filename] | croc send
</code></pre><p>In this case <em>croc</em> will automatically use the stdin data and send and assign a filename like &ldquo;croc-stdin-123456789&rdquo;. To receive to stdout at you can always just use the <code>-yes</code> and <code>-stdout</code> flags which will automatically approve the transfer and pipe it out to stdout.</p>
<pre tabindex="0"><code>$ croc -yes -stdout [code phrase] &gt; out
</code></pre><p>All of the other text printed to the console is going to <code>stderr</code> so it will not interfere with the message going to stdout.</p>
<h3 id="self-host-relay">Self-host relay</h3>
<p>The relay is needed to staple the parallel incoming and outgoing connections. The relay temporarily stores connection information and the encrypted meta information. The default uses a public relay at, <code>ws://198.199.67.130:8153</code>. You can also run your own relay, it is very easy, just run:</p>
<pre tabindex="0"><code>$ croc relay
</code></pre><p>Make sure to open up TCP ports (see <code>croc relay --help</code> for which ports to open). Relays can also be customized to which elliptic curve they will use (default is siec).</p>
<p>You can send files using your relay by entering <code>-relay</code> to change the relay that you are using if you want to custom host your own.</p>
<pre tabindex="0"><code>$ croc -relay &#34;ws://myrelay.example.com&#34; send [filename]
</code></pre><h3 id="configuration-file">Configuration file</h3>
<p>You can also make some paramters static by using a configuration file. To get started with the config file just do</p>
<pre tabindex="0"><code>$ croc config
</code></pre><p>which will generate the file that you can edit.
Any changes you make to the configuration file will be applied <em>before</em> the command-line flags, if any.</p>
]]></description>
    </item>
    
    <item>
	    <title>Raspberry Pi as a chicken cam</title>
	    <image>https://schollz.com/img/enclosure2.jpg</image>
      <link>/tinker/chickens/</link>
      <pubDate>Tue, 02 Oct 2018 19:10:48 -0700</pubDate>
      
      <guid>/tinker/chickens/</guid>
      <description><![CDATA[<p>ChickenCam was created because I feared the chickens were plotting against me. Here&rsquo;s how I subverted them with a Raspberry Pi based solution.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li><a href="http://amzn.to/2z1ObiI" target="_blank" >Raspberry Pi</a></li>
<li><a href="http://amzn.to/2yhhs9Q" target="_blank" >Camera without IR filter</a></li>
<li><a href="http://amzn.to/2zkm4vY" target="_blank" >Weatherproof extension cord</a> + <a href="http://amzn.to/2z3RENV" target="_blank" >Outlet tap</a></li>
<li><a href="http://amzn.to/2z2YGlR" target="_blank" >3D enclosure, specially fitted</a></li>
<li><a href="http://amzn.to/2zjpxLy" target="_blank" >Infrared illuminator</a></li>
<li><a href="http://amzn.to/2z1ZlEg" target="_blank" >USB microphone</a></li>
<li><a href="https://cse.google.com/cse?cx=008732268318596706411:nhtd4cwl5xu&amp;q=chickens&amp;oq=chickens&amp;gs_l=partner.3...1329.2438.0.2513.10.9.0.1.1.0.152.791.3j5.8.0.gsnos%2Cn%3D13...0.981j163459j9j1..1ac.1.25.partner..4.6.472.KwyGWJjj03s#gsc.tab=0&amp;gsc.q=chickens%20for%20sale&amp;gsc.sort=" target="_blank" >Chickens</a></li>
<li>Too much time on your hands</li>
</ul>
<h2 id="1-setup-raspberry-pi">1. Setup Raspberry Pi</h2>
<p>Plug in the USB microphone, install new image of <a href="https://www.raspberrypi.org/downloads/raspbian/" target="_blank" >Raspbian</a>, and attach the camera. Setup the camera using <code>raspi-config</code> and then setup the following.</p>
<h2 id="2-setup--wifihttpswwwraspberrypiorgdocumentationconfigurationwirelesswireless-climd">2. Setup  <a href="https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md" target="_blank" >Wifi</a></h2>
<p>Make sure this WiFi will work outside.</p>
<p><code>sudo vim /etc/wpa_supplicant/wpa_supplicant.conf</code></p>
<pre><code>network={
  ssid=&quot;SOMETHING&quot;
  psk=&quot;PASSWORD&quot;
}
</code></pre>
<h2 id="3-download-packages">3. Download packages</h2>
<pre tabindex="0"><code>sudo apt-get install apcalc python3 python3-setuptools zsh \
    openssh-server openssh-client tree git vim htop python3-pyaudio \
    python3-pil python3-numpy python3-rpio.gpio lame imagemagick
</code></pre><h2 id="4-setup-audiohttpraspberrypistackexchangecomquestions37177best-way-to-setup-usb-mic-as-system-default-on-raspbian-jessie">4. <a href="http://raspberrypi.stackexchange.com/questions/37177/best-way-to-setup-usb-mic-as-system-default-on-raspbian-jessie" target="_blank" >Setup audio</a></h2>
<p><code>sudo nano /usr/share/alsa/alsa.conf</code> scroll down until you find the lines</p>
<pre><code>defaults.ctl.card 0
defaults.pcm.card 0
</code></pre>
<p>and change them to</p>
<pre><code>defaults.ctl.card 1
defaults.pcm.card 1
</code></pre>
<h2 id="5-install-go">5. Install Go</h2>
<p>Download <a href="https://golang.org/dl/" target="_blank" >Go1.7+</a> and install.</p>
<h2 id="6-build-enclosure">6. Build enclosure</h2>
<p>Here&rsquo;s mine:</p>
<p><img src="/img/enclosure.jpg" alt=""></p>
<h2 id="7-start-chicken-monitoring">7. Start chicken monitoring</h2>
<p>On the Raspberry Pi, do the following:</p>
<pre tabindex="0"><code>git clone https://github.com/schollz/chickencam.git
cd chickencam
nano conf.py # edit SERVER_LOCATION with the your particular server
go build -o sunset
sudo python3 main.py
</code></pre><h2 id="8-start-web-server">8. Start web server</h2>
<p>This should be done on the server (which can also be the raspberry pi):</p>
<pre tabindex="0"><code>git clone https://github.com/schollz/chickencam.git
cd chickencam/server
go build
./server
</code></pre><h2 id="9-enjoy-your-chickens-popping-in-to-say-hello">9. Enjoy your chickens popping in to say hello</h2>
<p><img src="https://raw.githubusercontent.com/schollz/chickencam/master/server/static/img/poppingin.jpg" alt=""></p>
<h2 id="10-there-is-no-step-10">10. There is no step 10</h2>
]]></description>
    </item>
    
    <item>
	    <title>My Github graveyard</title>
	    <image>https://schollz.com/img/zerosones.jpg</image>
      <link>/tinker/graveyard/</link>
      <pubDate>Tue, 02 Oct 2018 07:16:21 -0700</pubDate>
      
      <guid>/tinker/graveyard/</guid>
      <description><![CDATA[<p>I like to write code and always have - my <a href="https://github.com/schollz/BandGenerator" target="_blank" >earliest project is a band name generator</a> from 2003. On Github  I have <a href="https://github.com/schollz?utf8=%E2%9C%93&amp;tab=repositories&amp;q=&amp;type=source&amp;language=" target="_blank" >over 230 repos</a>, but I also have some on <a href="https://bitbucket.org/schollz/" target="_blank" >Bitbucket</a>, <a href="https://gitlab.com/schollz" target="_blank" >Gitlab</a>, and my own <a href="https://fossil.schollz.com/" target="_blank" >self-hosted fossil</a>.</p>
<p>Like <a href="https://dev.to/t/graveyard" target="_blank" >others</a>, I&rsquo;ve had projects that I stopped working on and stopped using altogether - thus relegating them to the project graveyard. Here is a sliver of projects that ended up not meeting my expectations and have been buried in their grave of 1&rsquo;s and 0&rsquo;s.</p>
<p>&nbsp;</p>
<h2 id="-sundialhttpsgithubcomschollzsundial">🌅 <a href="https://github.com/schollz/sundial" target="_blank" >sundial</a></h2>
<p><em>Get the next sunset and sunrise time based on latitude and longitude.</em></p>
<p>I write most programs in Python and Go but I specifically wrote this one in C because I was planning on using it on an Arduino. I wanted an Arduino to know the time of sunrise and sunsets so it could be used to open/close a chicken coop door to let the chickens out at the right times.</p>
<p>The code here works great, however I when I put it on an Arduino it was always off by hours. I learned the error occurred because the <a href="https://www.arduino.cc/reference/en/language/variables/data-types/double/" target="_blank" >Uno boards use a double with 32-bit precision, not 64-bit</a> so there wasn&rsquo;t enough precision to actually make the mathematical calculations in the code. A hardware issue which I didn&rsquo;t see anyway around - oh well, to the grave you go.</p>
<p><strong>What I learned</strong>:</p>
<ul>
<li>Equations for calculating sunrise/sunset times.</li>
<li>Someone, somewhere, may find your code useful. I got a <a href="https://github.com/schollz/sundial/pull/1" target="_blank" >huge PR</a> out of the blue from someone who found this code immensely useful even though it had no use to me anymore.</li>
</ul>
<p>&nbsp;</p>
<h2 id="-food-identiconshttpsgithubcomschollzfood-identicon">🥚 <a href="https://github.com/schollz/food-identicon" target="_blank" >Food identicons</a></h2>
<p><em>A identicon made of foods.</em></p>
<p>I thought since people recognize food really well and are used to seeing recipes, that you could make an identicon that could uniquely identify some bytes with 9 pictures of food arranged in a square.</p>
<p>The program basically works, but it turned out to be hard to get nice pictures of food that are easily discernible so the identicons looked strange and indecipherable.</p>
<p><strong>What I learned</strong>:</p>
<ul>
<li>How hard it is to find and collect high quality images.</li>
<li>How to work with images in Go.</li>
</ul>
<p>&nbsp;</p>
<h2 id="-musicsaurhttpsgithubcomschollzmusicsaur">🎶 <a href="https://github.com/schollz/musicsaur" target="_blank" >musicsaur</a></h2>
<p><em>Music synchronization across browsers.</em></p>
<p>This is the one that saddens me the most. I was living in a two story house and I wanted to easily play music on both stories with two computers, so I wrote this program to synchronize music <em>playing through your browser</em>! It could could also play it headless on Raspberry Pis which was great fun.</p>
<p>I was never really satisfied that it worked <em>well enough</em>. Though it worked, every once and awhile you&rsquo;d get a song that had &gt;30 ms latency between the start times and then it would sound wonky. I used Javascript to control the skipping, but that is always problematic because Javascript is slow and can take ~0-10 ms to accurately skip to the position and it was near impossible to figure out exactly how much time is needed to account for the skip.</p>
<p>If you want to try, it still works, but you just need to use commit <code>b775910</code> from the <code>tcolgate/mp3</code> library since they made a breaking change since I&rsquo;ve played with it.</p>
<p><strong>What I learned</strong>:</p>
<ul>
<li>How hard it is to work with streaming audio.</li>
<li>How to use HTML5 audio in Javascript</li>
<li>Time synchronization routines</li>
</ul>
<p>&nbsp;</p>
<h2 id="-no-dicehttpsgithubcomschollzno-dice">🎲 <a href="https://github.com/schollz/no-dice" target="_blank" >No dice</a></h2>
<p><em>Generating random numbers from squiggles.</em></p>
<p>I had an idea of generating random numbers just by drawing quick squiggles and counting the intersections and then doing some modulus. Of course its slower than dice, but its better than nothing if you have no dice. I did some coding to figure out how &ldquo;random&rdquo; this can be, but realized that since humans are doing the drawing I&rsquo;d need to sample from actual humans instead of simulating human drawing with this code.</p>
<p><strong>What I learned</strong>:</p>
<ul>
<li>How hard it is to simulate human actions.</li>
<li>How to do bezier curves</li>
</ul>
<p>&nbsp;</p>
<h2 id="-bolhttpsgithubcomschollzbol">📘 <a href="https://github.com/schollz/bol" target="_blank" >bol</a></h2>
<p><em>A distributed, synchronized journal.</em></p>
<p>All my life I&rsquo;ve wanted to create the perfect distributed, synchronized journal. Nothing has ever satisfied. This is one attempt that I abandoned. It had a nice idea and worked across platforms, but I just didn&rsquo;t like using that much because of the UI and didn&rsquo;t have the will to make improve UX. I stopped using it so it became abandoned.</p>
<p>Since <a href="https://github.com/schollz/bol" target="_blank" >bol</a> I&rsquo;ve worked on a totally CLI version, <a href="https://github.com/schollz/gojot" target="_blank" >gojot</a>. However I&rsquo;ve abandoned and <a href="https://github.com/schollz/gojot" target="_blank" >rewritten gojot 5 times</a> and I still don&rsquo;t use it now. Lately I&rsquo;ve been pretty happy with a web version of another program I wrote, <a href="https://github.com/schollz/cowyo" target="_blank" >cowyo</a> but I&rsquo;ve actually found myself recently rewriting this one into <a href="https://rwtxt.com" target="_blank" >rwtxt</a>.</p>
<p><strong>What I learned:</strong></p>
<ul>
<li>How hard UI/UX design is and how hard I am to please</li>
<li>How hard it is to make distributed programs</li>
</ul>
<p>&nbsp;</p>
<h2 id="-kikihttpsgithubcomschollzkiki">👥 <a href="https://github.com/schollz/kiki" target="_blank" >kiki</a></h2>
<p><em>A distributed, offline-friendly social network.</em></p>
<p>There are all sorts of reasons to have alternatives to Facebook, so I decided to try to make one myself. I took a lot of inspiration from <a href="https://www.scuttlebutt.nz/" target="_blank" >scuttlebutt</a> but wanted to make something that was so easy to use that my mom could use it and wanted something that could be made private if I wanted to.</p>
<p>I had a lot of fun writing the code, but in the end I realized I wouldn&rsquo;t use it. To use it would encourage other people to use it and then I&rsquo;d be stuck with all the problems facing other social networks - how to regulate bad behavior while encouraging openness, etc. I had no desire to write code to regulate content but that what you have to do nowadays if you have a social network.</p>
<p><strong>What I learned:</strong></p>
<ul>
<li>How hard social networks are to maintain</li>
<li>Creating distributed message passing system</li>
</ul>
<p>&nbsp;</p>
<h2 id="-pypianohttpsgithubcomschollzpyplayerpiano">🎹 <a href="https://github.com/schollz/pyplayerpiano" target="_blank" >pypiano</a></h2>
<p><em>Let your computer play a piano duet with you.</em></p>
<p>Here is one happy story. I wanted to create an AI to play music with me at the piano. The obvious way to do this at the time was to use Python - they have a great gaming (<a href="https://www.pygame.org/wiki/GettingStarted" target="_blank" >pygame</a>) library which handles realtime MIDI out-of-the-box. It turned out, though, that development in Windows was super problematic and I found pygame would work with some functions but not with others (see my entire complaints here: <a href="https://rpiai.com/piano%29" target="_blank" >https://rpiai.com/piano)</a>. So I gave up and abandoned it.</p>
<p>But, this is a happy story because the project came back from the grave. I had an inclination it&rsquo;d be easier to write this in Go because it requires so many little async processes, easily done with goroutines. After a few more non-programming obstacles I was happily using the code: <a href="https://github.com/schollz/PIanoAI" target="_blank" >PianoAI</a>. Maybe its my favorite project I&rsquo;ve ever made.</p>
<p><strong>What I learned:</strong></p>
<ul>
<li>How hard it is to do realtime applications, especially with MIDI</li>
<li>How MIDI works</li>
<li>Heuristically learning synthetic piano lines</li>
</ul>
<h2 id="til-death-do-us-part">Til death do us part</h2>
<p>I could go on and on and on. At least I can say that I&rsquo;ve genuinely learned a lot from writing <em>and abandoning</em> code - trying (and maybe failing) is my favorite way to learn. I will continue to abandon projects, I think because I just like the journey more than the destination, even if that destination is the graveyard.</p>
]]></description>
    </item>
    
    <item>
	    <title>Ingredients</title>
	    <image>https://schollz.com/img/cookie.png</image>
      <link>/tinker/ingredients/</link>
      <pubDate>Mon, 16 Jul 2018 11:54:49 -0700</pubDate>
      
      <guid>/tinker/ingredients/</guid>
      <description><![CDATA[<div style="background-color: #fef7e0; padding:1em;">
<h2 id="try">Extract and tag ingredients from a website</h2>
<p>Extract ingredients from other recipe sites right in the browser!</p>
<p>
<select id="ingredientSelectInput" style="width:95%;padding:0.5em;">
<option value="">Select a cookie recipe</option>
<option value="https://www.crazyforcrust.com/best-chocolate-chip-cookie-recipe/">www.crazyforcrust.com/best-chocolate-chip-cookie-recipe/</option>
<option value=https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/>www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/</option>
<option value="https://joyfoodsunshine.com/the-most-amazing-chocolate-chip-cookies/">joyfoodsunshine.com/the-most-amazing-chocolate-chip-cookies/</option>
<option value="https://cakebycourtney.com/soft-chewy-chocolate-chip-cookies/">cakebycourtney.com/soft-chewy-chocolate-chip-cookies/</option>
<option value="https://laurenslatest.com/actually-perfect-chocolate-chip-cookies/">laurenslatest.com/actually-perfect-chocolate-chip-cookies//</option>
</select>
</p>
<p>Or input your own URL from any recipe website:
<input type="text" id="ingredientInput" value="https://cooking.nytimes.com/recipes/12320-apple-pie" style="width:80%;padding:1em;">
</p>
<div id="results">
<h4>JSON result:</h4>
<pre id="ingredientsJSON"></pre>
</div>
<div id="ingredientsWarn" style="color:#dc3545!important"></div>
<div id="ingredientsMessage" style="color: #6c757d!important;"></div>
<h4>From the command line:</h4>
<pre><code>$ curl https://faas.schollz.com/?url=https://cooking.nytimes.com/recipes/12320-apple-pie</code></pre>
<h4>Issue?</h4>
<p>Kindly provide me a report at <a href="https://github.com/schollz/ingredients/issues">Github</a>.</p>
</div>
<script src="/js/ingredients.js"></script>
<p>This blog post is related to a new Go library I wrote to extract and tag ingredients from recipes: <a href="https://github.com/schollz/ingredients" target="_blank" >ingredients</a>.</p>
<p>Recipes are curiosity to me. Recipes are essentially programs - a set of ingredients to be combined in a specific way using a series of instructions. Recipes have been around a lot longer than programs, though. Some have existed <a href="https://www.realmofhistory.com/2018/04/04/9-oldest-food-recipes-history/" target="_blank" >for thousands of years</a>. The oldest recipes still exist today which little modification but newer recipes are constantly evolving. In modern times, a single recipe may take on many forms as technologies progress, ingredients change, and cultural diets (paleo / dairy-free) flucuate.</p>
<p>Culinary recipes are everywhere on the Internet. I want to harness these to understand more about recipes. I want a tool that could utilize the vast trove of all recipes. What if you could instantly search and compare <em>thousands</em> of chocolate chip cookie recipes?</p>
<p>If there was a database of all known recipes you could easily search, index, you could compare ingredients, you could compare ratios of ingredients, you could look at variations of ingredients. I&rsquo;ve been working on making such a tool - a sort of &ldquo;Google&rdquo; for recipes - a huge indexed dataset of all recipes and their ingredients. This tool requires two components: firstly, I need a way to extract an ingredient list from any website and secondly, I need a way to tag the ingredients, measurements, and amounts from each line in the list.</p>
<h2 id="ingredient-tagging">Ingredient tagging</h2>
<p><em>Ingredient tagging</em> is where you take a line of ingredients and determine the ingredient name, measure, and amount. Here is an example of an ingredient line:</p>
<pre tabindex="0"><code>4 tablespoons melted nonhydrogenated margarine, melted coconut oil or canola oil
</code></pre><p>Tagging this ingredient line should yield the amount, measure, and name for the ingredient, for example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl">	<span class="nt">&#34;amount&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl">	<span class="nt">&#34;measure&#34;</span><span class="p">:</span> <span class="s2">&#34;tablespoons&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a></span><span class="cl">	<span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;margarine&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5">5</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This way, the ingredient can be used later on to compare to other ingredients based on it&rsquo;s name and measure/amount.</p>
<p>The prior art of ingredient tagging comes from the New York Times (NYT). The NYT wanted to <a href="https://open.blogs.nytimes.com/2015/04/09/extracting-structured-data-from-recipes-using-conditional-random-fields/" target="_blank" >extract ingredient data</a> for use with their recipe website. Their approach was to use <a href="https://en.wikipedia.org/wiki/Natural_language_processing" target="_blank" >Natural Language Processing</a>, and specifically <a href="https://homepages.inf.ed.ac.uk/csutton/publications/crftutv2.pdf" target="_blank" >linear-chain conditional random fields</a>. The NYT succeeded in creating <a href="https://github.com/nytimes/ingredient-phrase-tagger" target="_blank" >a tool</a> that was able to leverage structural prediction for ingredient tagging.</p>
<p>I spent a long time trying to improve on the NYT&rsquo;s ingredient tagger. I felt like the NLP approach was too general, especially when applied to something like ingredient lists. Its like taking a bazooka to kill a fly. I thought that there could be a simpler solution.</p>
<p>There are only finite number of ingredients (on the other of thousands) and ingredient lists are highly structured, 95% of the time. The structure highly pervasive, for example, an ingredient list will contain several or more ingredient lines, where each ingredient line will include an ingredient and usually a measure and an amount (of which usually come in specified order: amount, measure, ingredient). Using this insight I made a content-based extractor and parser, first <a href="https://github.com/schollz/extract_recipe" target="_blank" >in Python</a> and then later improved and written <a href="https://github.com/schollz/meanrecipe" target="_blank" >in Go</a> and now improved further with <a href="https://github.com/schollz/ingredients" target="_blank" >ingredients</a>.</p>
<p>Tagging of ingredients is done using the simplest possible way: greedy search and contextual finding. Greedy search means it will take the first possible choice, and contextual finding means that it will only start searching from the last position (in the context of each piece). For example, consider <code>1 1/2 cup (12 oz) mini chocolate chips</code> . To tag this I will first find the &ldquo;amount&rdquo;, then the &ldquo;measure&rdquo;, and finally the &ldquo;ingredient&rdquo;. The &ldquo;amount&rdquo; is the first consecutive list of numbers, for example, will see <code>1 1/2</code> as the first consecutive numbers (later computed as <code>1.5</code>). Next, you find the &ldquo;measure&rdquo; as the first measurement string after the location of the numbers. Of a list of measurement strings (e.g. <code>cup</code>,<code>tablespoon</code>,etc.) the first one that shows up is <code>cup</code> so that is selected. Finally, I used a corpus of ingredient names, sorted in decreasing order from the length of word. That way, the next match would be <code>chocolate chips</code>. Though <code>chocolate</code> also matches, it is not as long and the greedy search will take the longest match. From this simple set of guidelines we can easily match the majority of ingredient strings!</p>
<h2 id="ingredient-extraction-from-html">Ingredient extraction from HTML</h2>
<p>I hope to take a website - a set of HTML content - and extract only the ingredient list.  An ingredient list is the list of ingredients and their amounts that should be used to prepare the recipe. It usually comes before the directions and is someimtes encoded in HTML like the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl"><span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>Ingredients<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a></span><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3">3</a></span><span class="cl">	<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>1 cup chocolate chips<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4">4</a></span><span class="cl">	<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>1/2 cup melted butter<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5">5</a></span><span class="cl">	<span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>1 cup oats<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6">6</a></span><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>However, there is absolutely no guarantee that any random website will use <code>ul</code> tags or <code>li</code> tags, or any other tag. Also, this procedure is even more complicated by the fact that websites are completely bloated with other content that you do <em>not</em> want to extract.</p>
<p><img src="https://camo.githubusercontent.com/718ba18f178a4007bad4c8224cbb4ca817801737/68747470733a2f2f72706961692e66696c65732e776f726470726573732e636f6d2f323031352f30322f776562636f6e74656e74677261626265722d30312e706e67" alt="Website with ingredients"></p>
<p>My first attempt at this was to use content extraction. Basically I converted HTML to text and went line by line and computed how many &ldquo;ingredient&rdquo;-like words there word and then used the cluster of lines with ingredient words as a proxy for the ingredient list.</p>
<p><img src="https://camo.githubusercontent.com/73aab8a4b25a1af43a384fb67616d8c1914430be/68747470733a2f2f692e696d6775722e636f6d2f656e7530534e412e6a70673f31" alt=""></p>
<p>In practice this worked well, but not great. What could be improved?</p>
<p>Well, I realized recently that in doing this method I&rsquo;m essentially throwing away part of the information - the <em>structure</em> of the HTML. Even though I have no idea if the HTML encodes ingredient lists using <code>&lt;li&gt;</code> tags or <code>&lt;div&gt;</code> tags or whatever, I can still look for any <em>parent</em> tag that contains <em>child</em> tags that has the right kinds of words.</p>
<p>What are the right kind of words that designate that a given line is part of an ingredient list? This is where heuristics come in, based on some simple observations. Basically I made the following observations, and for every affirmative observation I add a +1 to a score for that line. A line with a high score is more likely an &ldquo;ingredient line&rdquo;.</p>
<h4 id="heuristics-for-establishing-an-ingredient-line">Heuristics for establishing an &ldquo;ingredient line&rdquo;</h4>
<ul>
<li>Does the line contain an ingredient word (apple, chocolate, etc.)?</li>
<li>Does the line contain a measure word (cups, tablespoon, etc.)?</li>
<li>Does the line contain a number?</li>
<li>Does the number come before the measure?</li>
<li>Does the measure come before the ingredient?</li>
<li>Is the line shorter than 100 characters?</li>
</ul>
<p>Most lines in an ingredient list will answer &ldquo;yes&rdquo; to these questions.</p>
<p>Thus, to find an ingredient list I can just parse an HTML tree using a depth-first search and calculating the score for each parent based on the how well the children answer each of those questions. Each parent above some threshold will be designated as the container for the ingredient list, after which the ingredient lines are quite easily determined.</p>
<h4 id="ingredient-extraction-from-json">Ingredient extraction from JSON</h4>
<p>There is a special case, that nowadays with progressive web apps, the recipe is contained in a JSON in a <code>&lt;script&gt;</code> tag and used dynamically to fill the HTML DOM with the data when the Javascript renders. To account for these pages I can do the same thing as above. First I get the JSON (by trial and error) and then parse the JSON and looking for an array of lines that look like ingredient lines.</p>
<h2 id="next-steps">Next steps</h2>
<p>In the future I will try to use this library to test whether I can collect enough information about enough recipes to make some interesting conclusions and ask questions about average recipes, recipe variations, and analysis of principal components of recipe types. I have thousands of chocolate chip cookie recipes ready to understand.</p>
]]></description>
    </item>
    
    <item>
	    <title>Self-hosting VCS with fossil</title>
	    <image>https://schollz.com/img/fossil.svg</image>
      <link>/tinker/fossil/</link>
      <pubDate>Sun, 10 Jun 2018 10:01:55 -0700</pubDate>
      
      <guid>/tinker/fossil/</guid>
      <description><![CDATA[<p>Github, Gitlab, Bitbucket are all web interfaces for <em>git</em>. They all do similar things that add on features that aren&rsquo;t implicit in <em>git</em>, like issues and wiki, etc.</p>
<p>Unbeknownst to some, <em>fossil</em> is an alternative to <em>git</em> that actually has built-in issues, wiki, and easily self-hosts. This is a quickstart to get started with hosting and cloning <em>fossil</em> repos. The <a href="http://www.fossil-scm.org/index.html/doc/2010-01-01/www/quickstart.wiki" target="_blank" >official quickstart</a> is very good, although I realized a few tricks in getting the <a href="#hosting" >fossil hosting to work on HTTPS</a> behind a reverse-proxy and getting <a href="#go" >fossil to work with Go</a>.</p>
<h2 id="before-you-begin">Before you begin</h2>
<p>First, download fossil. There are already <a href="https://www.fossil-scm.org/xfer/uv/download.html" target="_blank" >binaries available for <em>fossil</em></a>.</p>
<h2 id="new">New fossil</h2>
<p>Make sure to save each fossil repo with the extension <code>.fossil</code> - this is very important if you want to do hosting.</p>
<p>Now you can make a new fossil with the <code>init</code> subcommand.</p>
<pre tabindex="0"><code>&gt; fossil init -A schollz 3.fossil
project-id: 7165366dc9d0d3c827573c8ef12cb760f2caa236
server-id:  56adcebbf59b06eb4b621d3daca59f5aa87a9a08
admin-user: schollz (initial password is &#34;35a6g1&#34;)
</code></pre><p>This will make a new repository, <code>3.fossil</code>, with a user <code>schollz</code> and default password <code>35a6g1</code>. You can then change the default password for <code>schollz</code>. Here you have to enter the password on the command-line, but I like to add it to a file to prevent it from showing up in the bash history.</p>
<pre tabindex="0"><code>&gt; vim pass # enter your password in this file
&gt; fossil user password schollz `cat pass` -R 3.fossil
&gt; rm pass # this ensures your password doesn&#39;t enter the bash history
</code></pre><p>Want to add another user? Its also easy:</p>
<pre tabindex="0"><code>&gt; fossil user new user2 user2@somewhere.com -R 3.fossil
</code></pre><h2 id="hosting">Hosting fossils</h2>
<p>Say now you have several fossils in the same folder. Make sure that each has the <code>.fossil</code> suffix - this is very important.</p>
<pre tabindex="0"><code>&gt; ls fossils
1.fossil 2.fossil 3.fossil
</code></pre><p>Then you can start a server that uses HTTPS:</p>
<pre tabindex="0"><code>&gt; fossil server . --https --port 8079 --repolist
</code></pre><p>The <code>--https</code> flag implies HTTPS, so you have to host a reverse-proxy that uses SSL. Its super easy to do this if you are using <a href="https://caddyserver.com/download" target="_blank" >Caddy</a> as a reverse-proxy (which easily adds HTTPS). In this case your <a href="https://caddyserver.com/tutorial/caddyfile" target="_blank" >Caddyfile</a> will look like:</p>
<pre tabindex="0"><code>fossil.schollz.com {
        proxy / 127.0.0.1:8079 {
                transparent
        }
        gzip
        log logs/fossil.schollz.com.log
}
</code></pre><p>where you just replace <code>fossil.schollz.com</code> with <code>yourdomain.com</code>.</p>
<h2 id="cloning">Cloning fossil</h2>
<p>Cloning is easy, just make sure to include your user name so you will have the rights to push changes (unless you want to just have your own copy). If you are using &ldquo;<code>fossil server .</code>&rdquo; like above, then you need to add <code>/repo</code> to tell it you want to clone <code>repo.fossil</code>. For instance, to clone <code>3.fossil</code></p>
<pre tabindex="0"><code>fossil clone https://schollz@fossil.schollz.com/3 3.fossil
</code></pre><p>You will be prompted for the password, which is the same password that you set up top. Now you can open the repo.</p>
<pre tabindex="0"><code>&gt; fossil open 3.fossil
</code></pre><p>Things are then pretty similar to <em>git</em>. You can add and commit files:</p>
<pre tabindex="0"><code>&gt; touch README.md
&gt; fossil add README.md
&gt; fossil commit README.md # this will automatically push
</code></pre><p>You can easily create pull (get latest changes):</p>
<pre tabindex="0"><code>&gt; fossil sync
</code></pre><h2 id="access">Private and public repos</h2>
<h3 id="private-repos">Private repos</h3>
<p>It&rsquo;s pretty easy to make your fossil private. Just goto Admin -&gt; Security Audit and then click &ldquo;Take it private&rdquo;.</p>
<h3 id="public-repos">Public repos</h3>
<p>This is the default. However, sometimes if you take your repo private you&rsquo;d like to make it public again. Making your repository public again is a little more involved.</p>
<p>Basically there are two types of users: &ldquo;nobody&rdquo; and &ldquo;anonymous&rdquo; users. The &ldquo;anonymous&rdquo; refers to permissions given to <em>anyone who logs in</em>. The &ldquo;nobody&rdquo; refers to the permissions given to <em>anyone who visits</em> the site. To make a repo public again you have to redefine the permissions for both these types of users by going to Admin -&gt; Users.</p>
<p>The typical permissions for &ldquo;nobody&rdquo; for public access are &ldquo;gjozr&rdquo;:</p>
<p><img src="/img/nobody.PNG" alt="Permissions for public access for &ldquo;nobody&rdquo; (those who log in)."></p>
<p>The typical permissions for &ldquo;anonymous&rdquo; for public access are &ldquo;chmn&rdquo;:</p>
<p><img src="/img/chmn.PNG" alt="Permissions for public access for &ldquo;anonymous&rdquo; (those who log in)."></p>
<h2 id="go">Configure for <code>Go</code></h2>
<p>In order to use <em>fossil</em> with <code>go get</code> you need to make sure you have the right <a href="https://golang.org/cmd/go/#hdr-Remote_import_paths" target="_blank" >remote import paths</a> specified in the <code>meta</code> tag.</p>
<p>Basically, if you are following the above and you have a fossil repo hosted at <code>https://yourdomain.com/hello-world</code> then you need to include the meta tag:</p>
<pre tabindex="0"><code>&lt;meta name=&#34;go-import&#34; content=&#34;yourdomain.com/hello-world fossil https://yourdomain.com/hello-world&#34;&gt;
</code></pre><p>You can do this by changing the default fossil skin (Admin -&gt; Skins). There is a really nice <a href="http://fossil.include-once.org/fossil-skins/raw/googlecode.txt?name=1c1738c248dc1f5784e402a466e926bfd9a703e4" target="_blank" >Google Code skin</a> available. For convenience, I already have the <a href="https://cowyo.com/fossil_css/raw" target="_blank" >CSS</a>, <a href="https://cowyo.com/fossil_header2/raw" target="_blank" >Header</a>, and <a href="https://cowyo.com/fossil_footer/raw" target="_blank" >Footer</a>. Just edit each component (Step 4 under Skins) and then check both boxes in Step 7 and hit &ldquo;Publish Draft1&rdquo;.</p>
<h2 id="general">General</h2>
<p>I like to have the <code>README.md</code> in the main repo be the first thing you see on the web UI - just like Github/Gitlab/Bitbucket. To do this, make a <code>README.md</code> file and then goto Admin -&gt; Configuration and look for the <strong>Index Page</strong> and change it to <code>/doc/tip/README.md</code>. Make sure to then press &ldquo;Apply Changes&rdquo; at the top.</p>
<p>Another trick - you can add <code>fossil</code> as a parameter to your <code>~/.zshrc</code> if
you are using <em>oh-my-zsh</em> (if you aren&rsquo;t using it, why?). Just make sure
you have this line:</p>
<pre tabindex="0"><code>plugins=(git history fossil) 
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>Teaching statement</title>
	    <image>https://schollz.com</image>
      <link>/academic/teaching/</link>
      <pubDate>Sun, 10 Jun 2018 10:01:55 -0700</pubDate>
      
      <guid>/academic/teaching/</guid>
      <description><![CDATA[<p>Being a student for 20 years has taught me that students learn well when they love what they are learning. This simple aphorism – “you love to learn when you learn to love” – has been the cornerstone of my success as a student and a maturing teacher. Students used to regard classroom teachers as the singular resource for learning, but now students are surrounded by vast amounts of educational assets (textbooks, online lectures, YouTube videos, Wikipedia, etc.) which can make classroom teaching redundant or unnecessary (1). To circumvent the redundancy, I view the teaching position as a pylon to help students navigate these vast resources, to encourage an environment for intellectual interactions, and finally, to provide an enthusiasm that underpins joy of learning. These three aspects are crucial for students’ success in any variety of science classroom.</p>
<p>The teacher’s biggest obligation is to organize the knowledge in an accessible way. This is best done by laying out the resources beforehand and then providing an educational experience from your own perspective. As a student, my favorite teachers were those who presented the material that would be in addition to the reading assigned before class. Students benefit from this because it removes the repetition of reading the material at home and then hearing the same thing they read, later, in class. As teaching assistant for a recitation of “Thermodynamics for Engineers” I would have students work on homework problems before class and then present new problems to work on during class time after I had answered questions about the problems they did at home. This way, students were already exposed to the methods and equations and had crystallized questions that could be answered during class time when doing similar problems. The time the student has in the class with the teacher is precious and lesson plans should be considerate of this.</p>
<p>My most valuable time as a student was during active learning that promoted intellectual interactions. Large classrooms pose problems for active learning as students are often caught in group-think or otherwise too shy to voice their opinion and thus never resolve their questions adequately. Also, large groups make hands-on activities more difficult to implement. Both these issues can be resolved using small groups, as demonstrated by Nobel Laureate Carl Weiman recently with a controlled experiment (2). Retrospectively I found that these classes were also my favorites in high school and college. As a graduate student I have tried implementing these strategies during my teaching opportunities. I taught and developed syllabus during my tenure as a middle school science coach at B.O.O.S.T. (Building Opportunities and Overtures in Science and Technology) that grouped students into small sections to perform experiments and learn together (see group activities designed for developing research questions and designing experiments). The students really enjoyed these activities and often continued their learning at home although no homework was assigned and no teacher was present.</p>
<p>The resources to learn a subject are ubiquitous and it is possible for students to acquire knowledge without the presence of a teacher (1). However, in any educational program there are students who will not be self-motivated to inherently want to learn a subject. The teacher needs to break down this obstacle by providing an enthusiastic approach – this involves moving away from esotericism and moving towards more practical examples and problems that can directly relate to students. For instance, my physics professor told the class about how to make kelp into a musical instrument – which he related to his lecture about quantum physics. The professor’s simple anecdote exemplified his enthusiasm and hooked me and the rest of the students for the rest of the semester. As an undergraduate student research mentor I found that enthusiasm goes a long way to engage students. This is evident from three of my four student research apprentices have voluntarily returned to continue research under my guidance after spending a semester of independent study or summer of REU experience with me.</p>
<p>Teachers facilitate a key process in the education of the student. Though teachers are not the only facet in the educational system, the class time with a teacher is crucial for providing active learning experiences. I have learned from my past experiences as a TA, as an undergraduate research mentor, and as a middle school science coach, that the enthusiasm and the organization of a teacher facilitates the best environment for students to engage and love learning.</p>
<p>1. Mitra, Sugata, and Ritu Dangwal. &ldquo;Limits to self‐organising systems of learning—the Kalikuppam experiment.&rdquo; British Journal of Educational Technology 41.5 (2010): 672-688.</p>
<p>2. Smith, Michelle K., et al. &ldquo;Why peer discussion improves student performance on in-class concept questions.&rdquo; Science 323.5910 (2009): 122-124.</p>
]]></description>
    </item>
    
    <item>
	    <title>IPFS enabled personal internet archive</title>
	    <image>https://schollz.com/img/ipfs.jpg</image>
      <link>/tinker/ipfs-archive/</link>
      <pubDate>Mon, 04 Jun 2018 07:16:21 -0700</pubDate>
      
      <guid>/tinker/ipfs-archive/</guid>
      <description><![CDATA[<p>There are some great ways to archive sites on the web. You can use <a href="http://archive.is/" target="_blank" >archive.is</a> or the <a href="http://archive.org/web/" target="_blank" >Internet Archive</a>. But, using the IPFS, its really easy to make your own hosted internet archive.</p>
<p>Say you found a cool website, like <code>www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/</code> which shows how a Kalman filter works. All you need to do is recursively download the site, with cross-domain assets, and then pin it to IPFS.</p>
<pre tabindex="0"><code>$ mkdir site1 &amp;&amp; cd site1
$ wget --restrict-file-names=windows -k --adjust-extension --span-hosts --convert-links --backup-converted --page-requisites http://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/ 
$ cd ../ &amp;&amp; ipfs add -r site1 -Q
&lt;some hash&gt;
$ ipfs pin add -r &lt;some hash&gt;
</code></pre><p>That&rsquo;s it! IPFS will automatically resuse assets if they have the same hash, so common files (CSS, JS) will only ever be added once. You can then view your site with the IPFS gateway. For example, the site above is available at <a href="https://ipfs.io/ipfs/QmdLSyPnQLiVk1wy91tZvhVfqbFbD7zSCuRtC51Z9yeY8E/www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/" target="_blank" >https://ipfs.io/ipfs/QmdLSyPnQLiVk1wy91tZvhVfqbFbD7zSCuRtC51Z9yeY8E/www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Installing Keybase on Chromebook</title>
	    <image>https://schollz.comhttps://cdn-images-1.medium.com/max/1600/1*gOzphEYvIOe-MWFlzj6ihw.png</image>
      <link>/tinker/installing-keybase-on-chromebook/</link>
      <pubDate>Mon, 28 May 2018 10:28:00 -0700</pubDate>
      
      <guid>/tinker/installing-keybase-on-chromebook/</guid>
      <description><![CDATA[<p>Keybase is a great system for mounting a end-to-end encrypted filesystem.
It also has an encrypted git feature which is great for storing secure information in repositories.
While keybase has releases for most systems, they currently still <a href="https://github.com/keybase/keybase-issues/issues/2794#issuecomment-276076772" target="_blank" >don&rsquo;t create releases for arm</a> architectures.
I wanted to use Keybase on my chromebook, so with <a href="https://github.com/keybase/client/issues/8815#issuecomment-334397388%29" target="_blank" >help from the keybase developers</a> I have determined a way to get keybase up and running on a Chromebook in no time.</p>
<p>First install the pre-requisites:</p>
<pre tabindex="0"><code>$ sudo apt-get install fuse
</code></pre><p>Then install Keybase:</p>
<pre tabindex="0"><code>$ mkdir -p $GOPATH/src/github.com/keybase
$ cd $GOPATH/src/github.com/keybase
$ git clone --depth 1 https://github.com/keybase/client
$ go install -v -tags production github.com/keybase/client/go/keybase
</code></pre><p>Login with your keybase name (or add the computer if it isn&rsquo;t already added.</p>
<pre tabindex="0"><code>$ keybase login
</code></pre><p>Now install the <em>git-remote-keybase</em> for using git with keybase.</p>
<pre tabindex="0"><code>$ cd $GOPATH/src/github.com/keybase/client/go/kbfs/kbfsgit/git-remote-keybase
$ go install
</code></pre><p>If you&rsquo;d also like to mount directories you can install the keybase filesystem, <em>kbfsfuse</em>:</p>
<pre tabindex="0"><code>$ cd $GOPATH/src/github.com/keybase/client/go/kbfs/kbfsfuse
$ go install
$ sudo mkdir -p /keybase
$ sudo chown $USER /keybase
$ KEYBASE_RUN_MODE=prod kbfsfuse /keybase &amp; 
</code></pre><p>That&rsquo;s it! Enjoy.</p>
]]></description>
    </item>
    
    <item>
	    <title>My .vimrc</title>
	    <image>https://schollz.com/img/vim.jpg</image>
      <link>/tinker/vim/</link>
      <pubDate>Mon, 28 May 2018 10:25:45 -0700</pubDate>
      
      <guid>/tinker/vim/</guid>
      <description><![CDATA[<p>I&rsquo;ve been enjoying writing in <code>vim</code> a lot more.</p>
<p>There is now great support for writing code, especially Go. To get started with programming in Vim in Go, clone the latest <code>vim-go</code>:</p>
<pre tabindex="0"><code>$ git clone https://github.com/fatih/vim-go.git ~/.vim/pack/plugins/start/vim-go
</code></pre><p>Then run Vim and use the command <code>:GoInstallBinaries</code> which will install the necessary programs.</p>
<p>There is a nice and simple way to make writing a lot easier (word-wrapping, nice color scheme), add the a <code>WordProcessorMode</code>.</p>
<p>You can just make the following config file (save as <code>~/.vimrc</code>):</p>
<pre tabindex="0"><code>filetype plugin indent on
let g:go_fmt_command = &#34;goimports&#34;
func! WordProcessorMode()
  set columns=80
  setlocal formatoptions=1
  setlocal noexpandtab
  map j gj
  map k gk
  setlocal spell spelllang=en_us
  set complete+=s
  set formatprg=par
  setlocal wrap
  setlocal linebreak
  set foldcolumn=3
  highlight Normal ctermfg=black ctermbg=grey
  hi NonText ctermfg=grey guifg=grey
endfu
com! WP call WordProcessorMode()
</code></pre><p>Then, while in <code>vim</code> you can activate the word processing mode by typing <code>:WP</code>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Shallow `go get`</title>
	    <image>https://schollz.com/img/gopherwater.jpg</image>
      <link>/tinker/shallow-go-get/</link>
      <pubDate>Sun, 06 May 2018 07:00:57 -0700</pubDate>
      
      <guid>/tinker/shallow-go-get/</guid>
      <description><![CDATA[<h2 id="note-this-tutorial-is-deprecated-there-now-exists-modules-that-replace-the-need-for-this-hack">Note: this tutorial is deprecated. There now exists modules that replace the need for this hack.</h2>
<p>This is a little hack to use shallow clones for new git checkouts with <code>go get</code>. Unfortunately for Gophers, <a href="https://github.com/golang/go/issues/13078" target="_blank" >this has been an open issue for three years counting</a> without a workable solution aside from patching the go toolchain yourself. This solution utilizes a <em>git</em> wrapper that determines if a pull/clone is happening and then makes sure it is shallow.</p>
<p>The wrapper is only a few lines of code, here&rsquo;s the <code>main.go</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl">    <span class="s">&#34;os&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl">    <span class="s">&#34;os/exec&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">    <span class="nx">args</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Args</span>
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="p">&gt;</span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl">        <span class="k">if</span> <span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">&#34;pull&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl">            <span class="nx">args</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">args</span><span class="p">[:</span><span class="mi">2</span><span class="p">],</span> <span class="nb">append</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;--depth=1&#34;</span><span class="p">},</span> <span class="nx">args</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span><span class="o">...</span><span class="p">)</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">&#34;clone&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">            <span class="nx">args</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">args</span><span class="p">[:</span><span class="mi">2</span><span class="p">],</span> <span class="nb">append</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;--depth=1&#34;</span><span class="p">,</span> <span class="s">&#34;--shallow-submodules&#34;</span><span class="p">,</span> <span class="s">&#34;--single-branch&#34;</span><span class="p">},</span> <span class="nx">args</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span><span class="o">...</span><span class="p">)</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl">    <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">=</span> <span class="s">&#34;/usr/bin/git&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl">    <span class="nx">cmd</span> <span class="o">:=</span> <span class="nx">exec</span><span class="p">.</span><span class="nf">Command</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="p">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl">        <span class="nx">cmd</span> <span class="p">=</span> <span class="nx">exec</span><span class="p">.</span><span class="nf">Command</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a></span><span class="cl">    <span class="nx">cmd</span><span class="p">.</span><span class="nx">Stdout</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span>
</span></span><span class="line"><span class="ln" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a></span><span class="cl">    <span class="nx">cmd</span><span class="p">.</span><span class="nx">Stderr</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span>
</span></span><span class="line"><span class="ln" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a></span><span class="cl">    <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a></span><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a></span><span class="cl">        <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-27"><a class="lnlinks" href="#hl-0-27">27</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-28"><a class="lnlinks" href="#hl-0-28">28</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To make it easy, I&rsquo;ve saved this code as <code>main.go</code> in a repo named <code>git</code> (<a href="https://github.com/schollz/git" target="_blank" >schollz/git</a>). The repo is named &ldquo;<code>git</code>&rdquo; on purpose so that your GOPATH can be prepended to the PATH and then the wrapper can be substituted for the real <code>git</code> (<code>/usr/bin/git</code>). So then, to activate shallow cloning all you have to do is:</p>
<pre tabindex="0"><code>$ go get github.com/schollz/git
$ export PATH=$GOPATH/bin:$PATH
</code></pre><p>which you can add to your <code>.bashrc</code> files if you want it to be permanent. This way, the wrapper will aways be used and the wrapper will force cloning to be shallow.</p>
<h2 id="benchmarks">Benchmarks</h2>
<p>Here&rsquo;s a benchmark showing a 50% reduction in disk usage and thus a 50% reduction in time taken for a <code>go get</code>. You&rsquo;ll not get that much for smaller repositories, but its not bad.</p>
<h3 id="normal-go-get">normal <code>go get</code></h3>
<pre tabindex="0"><code>% docker run -it golang:1.10 /bin/bash
root@d9208178f1fa:/go# time go get github.com/juju/juju/...
real    7m35.631s
user    1m40.059s
sys     0m45.436s
root@d9208178f1fa:/go# du -sh .
1.1G
</code></pre><h3 id="shallow-go-get">shallow <code>go get</code></h3>
<pre tabindex="0"><code>% docker run -it golang:1.10 /bin/bash
root@68135fb64a3e:/go# go get github.com/schollz/git
root@68135fb64a3e:/go# export PATH=$GOPATH/bin:$PATH
root@68135fb64a3e:/go# time go get github.com/juju/juju/...
real    3m0.335s
user    0m29.192s
sys     0m17.253s
root@d9208178f1fa:/go# du -sh .
499M    .
</code></pre><p>Thanks <a href="https://github.com/tscholl2" target="_blank" >tscholl2</a> for the idea.</p>
]]></description>
    </item>
    
    <item>
	    <title>Peer discovery</title>
	    <image>https://schollz.com/img/gopherpeer.png</image>
      <link>/tinker/peerdiscovery/</link>
      <pubDate>Mon, 23 Apr 2018 07:50:07 -0700</pubDate>
      
      <guid>/tinker/peerdiscovery/</guid>
      <description><![CDATA[<p>Pure-go library for cross-platform thread-safe local peer discovery using UDP multicast. I needed to use peer discovery for <a href="https://github.com/schollz/croc" target="_blank" >croc</a> and everything I tried had problems, so I made another one.</p>
<h2 id="install">Install</h2>
<p>Make sure you have Go 1.5+.</p>
<pre tabindex="0"><code>go get -u github.com/schollz/peerdiscovery
</code></pre><h2 id="usage">Usage</h2>
<p>The following is a code to find the first peer on the local network and print it out.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="nx">discoveries</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">peerdiscovery</span><span class="p">.</span><span class="nf">Discover</span><span class="p">(</span><span class="nx">peerdiscovery</span><span class="p">.</span><span class="nx">Settings</span><span class="p">{</span><span class="nx">Limit</span><span class="p">:</span> <span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl"><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">d</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">discoveries</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;discovered &#39;%s&#39;\n&#34;</span><span class="p">,</span> <span class="nx">d</span><span class="p">.</span><span class="nx">Address</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here&rsquo;s the output when running on two computers. (<em>Run these gifs in sync by hitting Ctl + F5</em>).</p>
<p><strong>Computer 1:</strong></p>
<p><img src="https://user-images.githubusercontent.com/6550035/39165714-ba7167d8-473a-11e8-82b5-fb7401ce2138.gif" alt="computer 1"></p>
<p><strong>Computer 2:</strong></p>
<p><img src="https://user-images.githubusercontent.com/6550035/39165716-ba8db9ec-473a-11e8-96f7-e8c64faac676.gif" alt="computer 1"></p>
<p>For more examples, see the scanning examples (<a href="https://github.com/schollz/peerdiscovery/blob/master/examples/ipv4/main.go" target="_blank" >ipv4</a> and <a href="https://github.com/schollz/peerdiscovery/blob/master/examples/ipv6/main.go" target="_blank" >ipv6</a>) or <a href="https://pkg.go.dev/github.com/schollz/peerdiscovery" target="_blank" >the docs</a>.</p>
<h2 id="testing">Testing</h2>
<p>To test the peer discovery with just one host, one can launch multiple containers. The provided <code>Dockerfile</code> will run the example code.
Please make sure to enable <a href="https://docs.docker.com/v17.09/engine/userguide/networking/default_network/ipv6/" target="_blank" >Docker&rsquo;s IPv6 support</a> if you are using IPv6 for peer discovery.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1">1</a></span><span class="cl"><span class="gp">#</span> Build the container, named peertest
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2">2</a></span><span class="cl"><span class="gp">$</span> docker build -t peertest .
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3">3</a></span><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4">4</a></span><span class="cl"><span class="err"></span><span class="gp">#</span> Execute the following <span class="nb">command</span> in multiple terminals
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5">5</a></span><span class="cl"><span class="gp">$</span> docker run -t --rm peertest
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6">6</a></span><span class="cl"><span class="go">Scanning for 10 seconds to find LAN peers
</span></span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7">7</a></span><span class="cl"><span class="go"> 100% |████████████████████████████████████████|  [9s:0s]Found 1 other computers
</span></span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8">8</a></span><span class="cl"><span class="go">0) &#39;172.17.0.2&#39; with payload &#39;zqrecHipCO&#39;
</span></span></span></code></pre></div><h2 id="contributing">Contributing</h2>
<p>Pull requests are welcome. Feel free to&hellip;</p>
<ul>
<li>Revise documentation</li>
<li>Add new features</li>
<li>Fix bugs</li>
<li>Suggest improvements</li>
</ul>
<h2 id="thanks">Thanks</h2>
<p>Thanks <a href="https://github.com/geistesk" target="_blank" >@geistesk</a> for adding IPv6 support and a <code>Notify</code> func, and helping maintain! Thanks <a href="https://github.com/Kunde21" target="_blank" >@Kunde21</a> for providing a bug fix and massively refactoring the code in a much better way. Thanks <a href="https://github.com/robpre" target="_blank" >@robpre</a> for finding and fixing bugs. Thanks <a href="https://github.com/shvydky" target="_blank" >@shvydky</a> for adding dynamic payloads.</p>
]]></description>
    </item>
    
    <item>
	    <title>10 years of marathons</title>
	    <image>https://schollz.com/img/marathons.jpg</image>
      <link>/tinker/marathons-2018/</link>
      <pubDate>Fri, 23 Mar 2018 23:07:45 -0700</pubDate>
      
      <guid>/tinker/marathons-2018/</guid>
      <description><![CDATA[<p>I&rsquo;ve done dozens of races and about 15 marathons since I started running. I&rsquo;ve plotted my pace for 15 official marathons. Its quite obvious now that I&rsquo;ve plateaued at a nominal pace.  You can see that I got fast quite quickly - after about three years I hit my plateau of about 8 minutes per mile.</p>
<p><img src="/img/marathon_pacing.png" alt="My pacing in the past 15 marathons"></p>
<p>In my first three years of running I ran about 20 miles a week - sometimes more, sometimes less. I started running fast and a year of intense training about five years in, which got the lowest dot on that graph - a sub 7 minute per mile marathon (which qualified me for the Boston marathon).</p>
<p>I also computed my relative placement in marathons to see how I did compared to others. The relative placement is my place overall divided by the number of people in the race. A relative placement of 1 means that I finished last, and 0.5 means I finished right in the middle.</p>
<p><img src="/img/marathon_placement.png" alt="My placement in the past 15 marathons"></p>
<p>You can see that over those first three years my relative placement plunged a lot more than my pace. This shows that you can do a lot better in a race by just increasing your pace a little bit. Currently I&rsquo;m able to hover around the top 15% of any given race.</p>
<p>I think this is a nice illustration of being able to work for something you want. I wanted to become a marathon runner, so I ran. I started out bad (finishing in the last 20% of the my first marathon) but over three years you can see I gradually improved. You can also see that those improvements have lasted over the years, as I continue to run casually.</p>
]]></description>
    </item>
    
    <item>
	    <title>Consensus cookery</title>
	    <image>https://schollz.com/img/cookies.jpg</image>
      <link>/tinker/consensus-cookery/</link>
      <pubDate>Tue, 20 Mar 2018 09:41:01 -0600</pubDate>
      
      <guid>/tinker/consensus-cookery/</guid>
      <description><![CDATA[<p>Sometimes when I want a recipe to cook something new I will find several recipes for the same thing and try to use them as a guide to generate an average or &ldquo;consensus&rdquo; recipe. This code should make it easy to generate consensus recipes (useful!) and also show variation between recipes (interesting!).</p>
<p>Finding a consensus recipe requires first clustering many recipes. This is because a single recipe (<em>e.g.</em> a recipe for brownies) might have many significant variations (<em>e.g.</em> brownies can have just cocoa, just chocolate, or both). This code will first cluster recipes and then use the clusters to deliver the consensus recipe.</p>
<h2 id="example">Example</h2>
<p>The <em>quick-and-dirty</em> implementation goes like this:</p>
<ol>
<li>Choose a recipe (<em>e.g.</em> brownies, crepes, pancakes).</li>
<li>Search using duckduckgo.com to find hundreds of corresponding recipes (<code>fetch_urls.js</code>).</li>
<li>Download all the recipes and use <code>pandoc</code> to convert to text for processing.</li>
<li>Use a really simple (read: <em>bad</em>) context-extractor to grab ingredients.</li>
<li>Cluster the recipes based on the presence of ingredients.</li>
<li>Take the median values for ingredients in a given cluster to create an average recipe.</li>
</ol>
<p>The context-extractor works by finding the most likely &ldquo;ingredient&rdquo; section in the web page and then trying to parse those ingredients using a greedy search from a list of likely ingredients (<code>top_5k.txt</code>). Its not a great implementation. However, the errors in it are pretty random, which means you can get okay results as long as you have ~hundreds of recipes.</p>
<p>The median values are used, rather than the mean, so its less susceptible to bad parsing of the quantity. Again, as long as the parser is okay, it should be accurate enough.</p>
<p>Here&rsquo;s some examples of running the code (check out the code <a href="https://github.com/schollz/consensus-cookery" target="_blank" >on Github</a>).</p>
<h3 id="brownies">Brownies</h3>
<p>As mentioned, brownies are sometimes made with cocoa, sometimes chocolate, and sometimes both. Interestingly the machine learning automatically detects this.</p>
<p>Here&rsquo;s the biggest &ldquo;brownie&rdquo; cluster which shows ingredients for a consensus recipe made with chocolate (made up of 44 recipes). The <code>Rel. Freq.</code> corresponds to the percentage of recipes that contain that ingredient.</p>
<pre tabindex="0"><code>cluster 0 (n=44)
+------------+-------------+------------+
| Ingredient |    Amount   | Rel. Freq. |
+------------+-------------+------------+
|   butter   |  4 1/2 tbsp |     98     |
| chocolate  |  4 1/2 tbsp |     93     |
|    eggs    | 1 5/8 whole |     93     |
|   flour    |  6 3/4 tbsp |     80     |
|    salt    |   1/4 tsp   |     50     |
|   sugar    |   3/4 cup   |     91     |
|  vanilla   |   1/2 tsp   |     70     |
+------------+-------------+------------+
</code></pre><p>The next biggest cluster shows ingredients for a brownie recipe that is made with cocoa powder. (Also it uses baking powder unlike the previous recipe).</p>
<pre tabindex="0"><code>cluster 11 (n=28)
+---------------+------------+------------+
|   Ingredient  |   Amount   | Rel. Freq. |
+---------------+------------+------------+
| baking powder |  1/4 tsp   |     86     |
|     cocoa     | 3 1/4 tbsp |     71     |
|      eggs     | 1.0 whole  |     57     |
|     flour     |  5.0 tbsp  |     93     |
|      salt     |  1/4 tsp   |     79     |
|     sugar     | 7 1/4 tbsp |     93     |
|    vanilla    |  1/4 tsp   |     68     |
+---------------+------------+------------+
</code></pre><p>The third biggest cluster shows ingredients for a brownie recipe that uses both chocolate <em>and</em> cocoa.</p>
<pre tabindex="0"><code>cluster 4 (n=28)
+-------------+-------------+------------+
|  Ingredient |    Amount   | Rel. Freq. |
+-------------+-------------+------------+
| brown sugar |   6.0 tbsp  |    100     |
|    butter   |  6 3/4 tbsp |    100     |
|  chocolate  |  6 1/2 tbsp |     89     |
|    cocoa    |  5 3/4 tbsp |     54     |
|     eggs    | 1 7/8 whole |    104     |
|    flour    |   1/2 cup   |     89     |
|     salt    |   3/8 tsp   |     86     |
|    sugar    |   1/2 cup   |    100     |
|   vanilla   |   3/8 tsp   |    100     |
+-------------+-------------+------------+
</code></pre><p>You may notice that the proportions are odd (<code>1 7/8 eggs</code>!) which is because the program tries to normalize the recipes to a specified volume, and then converts them back to the median volume in all the recipe cluster.</p>
<h3 id="pancakes">Pancakes</h3>
<p>The machine learning clustering highlights the major difference between pancakes - whether they are buttermilk or not. These are the first two biggest clusters, where the first one has milk and the second uses buttermilk.</p>
<pre tabindex="0"><code>cluster 15 (n=33)
+---------------+-------------+------------+
|   Ingredient  |    Amount   | Rel. Freq. |
+---------------+-------------+------------+
| baking powder |   1/8 tsp   |    100     |
|     butter    |   1/2 tsp   |    103     |
|      eggs     | 1 1/4 whole |     97     |
|     flour     |  1 1/4 cup  |    100     |
|      milk     |  1 1/8 cup  |     94     |
|      salt     |   1/2 tsp   |     94     |
|     sugar     |  1 1/2 tsp  |    100     |
+---------------+-------------+------------+
</code></pre><pre tabindex="0"><code>cluster 14 (n=29)
+---------------+-------------+------------+
|   Ingredient  |    Amount   | Rel. Freq. |
+---------------+-------------+------------+
| baking powder |   5/8 tsp   |    100     |
|  baking soda  |   1/2 tsp   |     97     |
|     butter    |   1/2 tsp   |    100     |
|   buttermilk  |  1 1/4 cup  |     97     |
|      eggs     | 1 1/8 whole |     97     |
|     flour     |  1 1/8 cup  |    100     |
|      salt     |   3/8 tsp   |     90     |
|     sugar     |  1 1/4 tsp  |    103     |
|    vanilla    |   5/8 tsp   |     41     |
+---------------+-------------+------------+
</code></pre><h3 id="homemade-noodles">Homemade noodles</h3>
<p>The machine learning clustering picks up on an important distinction within noodle making - whether to use semolina <em>or</em> flour.</p>
<pre tabindex="0"><code>cluster 18 (n=24)
+------------+-------------+------------+
| Ingredient |    Amount   | Rel. Freq. |
+------------+-------------+------------+
|    eggs    | 2 1/2 whole |     83     |
|   flour    |  2 3/8 cup  |    129     |
|    salt    |   5/8 tsp   |     75     |
|   water    |  6 3/8 tbsp |    100     |
+------------+-------------+------------+
</code></pre><pre tabindex="0"><code>cluster 14 (n=16)
+------------+------------+------------+
| Ingredient |   Amount   | Rel. Freq. |
+------------+------------+------------+
|    eggs    | 2.0 whole  |    112     |
|   flour    | 1 3/8 cup  |    119     |
| olive oil  | 2 7/8 tsp  |     94     |
|    salt    |  5/8 tsp   |     75     |
|  semolina  |  1.0 cup   |     31     |
|   water    | 3 7/8 tbsp |     75     |
+------------+------------+------------+
</code></pre><h3 id="hamburger">Hamburger</h3>
<p>Here&rsquo;s a funny thing. If you are not too specific about the recipe you want, you might get clusters of truly different recipes. Consider the hamburger.</p>
<p>The biggest cluster for hamburger is obviously a list of ingredients for a hamburger recipe albeit the proportions are off (you can just multiple the amounts by some factor).</p>
<pre tabindex="0"><code>+----------------------+------------+------------+
|      Ingredient      |   Amount   | Rel. Freq. |
+----------------------+------------+------------+
|         beef         | 3 5/8 tbsp |     87     |
|         eggs         | 3/8 whole  |     33     |
|        garlic        | 6 1/2 tbsp |     77     |
|        onion         | 4 1/8 tbsp |     50     |
|         salt         |  1/4 tsp   |     40     |
| worcestershire sauce |  1.0 tsp   |     47     |
+----------------------+------------+------------+
</code></pre><p>Interestingly, one of the next biggest clusters is not a hamburger - it has no meat in it! Looking at it closer though it is obviously a hamburger <em>bun</em> recipe, which the machine learning clustering automatically detected. Lol.</p>
<pre tabindex="0"><code>+------------+-----------+------------+
| Ingredient |   Amount  | Rel. Freq. |
+------------+-----------+------------+
|   butter   |  1.0 tsp  |     53     |
|    eggs    | 7/8 whole |     79     |
|   flour    | 2 3/8 cup |     95     |
|    milk    |  3/4 cup  |     37     |
|    salt    |  7/8 tsp  |     74     |
|   sugar    | 3 1/8 tsp |     95     |
|   water    |  5/8 cup  |     79     |
|   yeast    |  5/8 tsp  |     79     |
+------------+-----------+------------+
</code></pre><h1 id="try-it">Try it</h1>
<p>I decided to try out my software in the real world. What would one of these average recipes taste like? To see, I computed the average recipes for &ldquo;chocolate chip cookies&rdquo; and took the second largest cluster because it had both baking powder and baking soda.</p>
<p><strong>The computed average chocolate chip cookie recipe:</strong></p>
<table>
<thead>
<tr>
<th>Ingredient</th>
<th>Amount</th>
<th>Variation</th>
<th>Rel. Freq.</th>
</tr>
</thead>
<tbody>
<tr>
<td>baking powder</td>
<td>1 tsp</td>
<td>± 1 3/8</td>
<td>95</td>
</tr>
<tr>
<td>baking soda</td>
<td>3/4 tsp</td>
<td>± 3/8</td>
<td>75</td>
</tr>
<tr>
<td>brown sugar</td>
<td>7/8 cup</td>
<td>± 3/8</td>
<td>99</td>
</tr>
<tr>
<td>butter</td>
<td>5/8 cup</td>
<td>± 1/2</td>
<td>97</td>
</tr>
<tr>
<td>chocolate</td>
<td>1 cup</td>
<td>± 5/8</td>
<td>109</td>
</tr>
<tr>
<td>eggs</td>
<td>2 whole</td>
<td>± 3/4</td>
<td>105</td>
</tr>
<tr>
<td>flour</td>
<td>1 1/4 cup</td>
<td>± 7/8</td>
<td>116</td>
</tr>
<tr>
<td>salt</td>
<td>5/8 tsp</td>
<td>± 1/2</td>
<td>86</td>
</tr>
<tr>
<td>sugar</td>
<td>3/8 cup</td>
<td>± 1/4</td>
<td>100</td>
</tr>
<tr>
<td>vanilla</td>
<td>1 5/8 tsp</td>
<td>± 3 1/8</td>
<td>100</td>
</tr>
</tbody>
</table>
<p>I used my standard techniques for baking to mix up the ingredients - first mixing wet and then adding dry ingredients and then baking for 10-15 minutes at 350F. They turned out to be much more like cake than cookies. Apparently there was too much baking powder and the ratio of liquid to dry ingredients was too high. They also tasted too sugary. They weren&rsquo;t bad, but they weren&rsquo;t great, so I think they would qualify as <em>average cookies</em>.</p>
<p><img src="/img/cookies.jpg" alt="Average cookies I made from my code results"></p>
<p>I think part of the problem was that I had trouble converting ingredients to volumes for normalization. Some recipes dictate their recipes in &ldquo;grams&rdquo; or &ldquo;ounces&rdquo; which need to be converted to volume using the density. In this version I used a constant density for everything (0.9 g / ml) which was somewhat between the density for flour and water. However the density for flour (0.6 g / ml) is much lower than the density for water (1 g / ml) and butter (0.9 g / ml).</p>
<p>When I modified the densities, it indeed changed the flour to  1 3/4 cup instead of 1 1/4 cup, and reduced the variation from 7/8 cup to 1/2 cup. Next time I think I&rsquo;d like to make the biggest cluster - i.e. the most popular recipe, which doesn&rsquo;t use baking powder. Here&rsquo;s that recipe:</p>
<table>
<thead>
<tr>
<th>Ingredient</th>
<th>Amount</th>
<th>Variation</th>
<th>Rel. Freq.</th>
</tr>
</thead>
<tbody>
<tr>
<td>baking soda</td>
<td>7/8 tsp</td>
<td>± 3/8</td>
<td>97</td>
</tr>
<tr>
<td>brown sugar</td>
<td>3/4 cup</td>
<td>± 1/4</td>
<td>91</td>
</tr>
<tr>
<td>butter</td>
<td>3/4 cup</td>
<td>± 3/8</td>
<td>99</td>
</tr>
<tr>
<td>chocolate</td>
<td>1 3/8 cup</td>
<td>± 5/8</td>
<td>105</td>
</tr>
<tr>
<td>eggs</td>
<td>2 whole</td>
<td>± 1/2</td>
<td>103</td>
</tr>
<tr>
<td>flour</td>
<td>2 cup</td>
<td>± 1/2</td>
<td>96</td>
</tr>
<tr>
<td>salt</td>
<td>5/8 tsp</td>
<td>± 3/8</td>
<td>89</td>
</tr>
<tr>
<td>sugar</td>
<td>1/2 cup</td>
<td>± 1/4</td>
<td>94</td>
</tr>
<tr>
<td>vanilla</td>
<td>1 1/4 tsp</td>
<td>± 2 1/8</td>
<td>98</td>
</tr>
</tbody>
</table>
<p>In this case the flour seems a lot more reasonable too (2 cups). I&rsquo;d be interested in trying this recipe, instead.</p>
<p>If you&rsquo;d like to generate your own average recipes, check out <a href="https://github.com/schollz/consensus-cookery" target="_blank" >the source on Github</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Hackers and Painter</title>
	    <image>https://schollz.com</image>
      <link>/books/hackers/</link>
      <pubDate>Wed, 21 Feb 2018 09:43:35 -0600</pubDate>
      
      <guid>/books/hackers/</guid>
      <description><![CDATA[<p>I consider myself a hacker, in the past year I wrote a <a href="https://github.com/search?l=&amp;o=desc&amp;q=user%3Aschollz&#43;created%3A%3E2017-04-29&amp;s=stars&amp;type=Repositories" target="_blank" >lot of code</a>, which range from the useful<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> to the useless.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> I also consider myself a painter - in the past year I&rsquo;ve also painted dozens of <a href="/painting/" >paintings</a> - large and small. In learning to code and learning to paint I&rsquo;ve noticed some similarities that resonated with me while reading this book.</p>
<p>I was really excited to read this book <a href="https://findabooktoread.com/book/330196745-hackers-and-painters-big-ideas-from-the-computer-age-by-paul-graham" target="_blank" ><em>Hackers and Painters by Paul Graham</em></a>. In this book, Paul Graham points how some of the similarities between painting and coding. Here is my short and naive summary:</p>
<p>Hackers and painters&hellip;</p>
<ul>
<li>&hellip;both utilize past references (museum for painters, open-source for programmers).</li>
<li>&hellip;both require learning by doing.</li>
<li>&hellip;both allow building on what you learned previously.</li>
<li>&hellip;both developthrough gradual refinement.</li>
<li>&hellip;both develop human audience and require seeing something from another&rsquo;s point of view.</li>
</ul>
<p>I wish the book looked more at the differences between painting and coding. In my opnion, I think they are quite interesting. For example, in painting the end result is nearly all that matters, whereas in coding, people will criticize all sorts of meta-aspects (commit descriptions, coding style, etc.).</p>
<p>One more interesting difference between painting and coding is that in coding, a little mistake can create new and amazing content in the painting. However, in coding a mistake often is a nucleus for more mistakes.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I wrote a useful framework for <a href="https://github.com/schollz/find3" target="_blank" >indoor positioning</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Can random squiggles generate random numbers? <a href="https://github.com/schollz/no-dice" target="_blank" >https://github.com/schollz/no-dice</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Podcasts I heard</title>
	    <image>https://schollz.com/img/podcasts.jpg</image>
      <link>/tinker/podcasts-2017/</link>
      <pubDate>Fri, 15 Dec 2017 09:41:01 -0600</pubDate>
      
      <guid>/tinker/podcasts-2017/</guid>
      <description><![CDATA[<p>I&rsquo;ve spent days sitting in a dark room looking into a microscope. Lots of
the time I listen to music, but I&rsquo;ve started to listen to podcasts. I&rsquo;ve
found a lot of great podcasts out there. I think my number one category of
podcast is “<em>the podcast about the small things</em>.” Its always
a welcome relief from the mainstream media which focuses more on the
hyperbole of small things. I do like some news, but I&rsquo;ve found some great
podcasts that pitch the news in a more informal and friendly way. These
are my favorite podcasts discovered this year, I hope I continue to
discover new ones.</p>
<p>The basic premise of <em>the podcast about the small things</em> is always to find
some small thing that no one thinks about and talk about it for 20-30
minutes and often interview the main players involved in the production
of the Small Things. I think the major bastion of this type of podcast is
<strong><a href="https://99percentinvisible.org/" target="_blank" >99% invisible</a></strong>, which is devoted to
illuminating <em>&ldquo;all the thought that goes into the things we don’t think
about.&rdquo;</em></p>
<p>There seems to be a cottage industry for these types of podcasts, and
I love them all. There is <strong><a href="https://gimletmedia.com/every-little-thing/" target="_blank" >Every Little
Thing</a></strong> which is <em>&ldquo;a show
about the small stuff.&rdquo;</em> There is <strong><a href="http://revisionisthistory.com/about" target="_blank" >Revisionist
History</a></strong> which is Malcom Gladwell&rsquo;s
show about <em>&ldquo;Something overlooked. Something misunderstood.&rdquo;</em> but mostly
its just small things that Mr. Gladwell finds fascinating. There is
<strong><a href="http://www.undiscoveredpodcast.org/" target="_blank" >Undiscovered</a></strong> which is a podcast
<em>&ldquo;about the all the stuff that gets left out of journal articles,&rdquo;</em> and
while nominally science-based it often seeks out big stories from small
things. There is <strong><a href="https://www.20k.org/" target="_blank" >Twenty Thousand Hertz</a></strong> which
is more sound-based but still aims to fill the divide because <em>&ldquo;you may
not think about the sounds you hear every day.&rdquo;</em> There is
<strong><a href="http://www.npr.org/podcasts/510307/invisibilia" target="_blank" >Invisibilia</a></strong> which
<em>&ldquo;is about the invisiable forces that control human behavior&rdquo;</em> and then
there is <strong><a href="https://gimletmedia.com/surprisingly-awesome/" target="_blank" >Surprisingly
Awesome</a></strong> which attempts
to find fascination in the world even though <em>&ldquo;lots of things in the world
seem ordinary.&rdquo;</em> For a more food-related one there is <strong><a href="http://www.cbc.ca/mediacentre/program/the-fridge-light" target="_blank" >The Fridge
Light</a></strong> which
looks at <em>&ldquo;the hidden stories of the things we eat.&rdquo;</em> Another food one is
<strong><a href="http://creative.gimletmedia.com/show/why-we-eat-what-we-eat/about/" target="_blank" >Why We Eat What We
Eat</a></strong>
which <em>&ldquo;investigates the unseen forces that shape our eatings habits.</em>&rdquo;
Though its not around anymore, I loved listening to <strong><a href="https://gimletmedia.com/mystery-show/" target="_blank" >Mystery
Show</a></strong> that simply <em>&ldquo;solves
a minor mystery which cannot be solved with search engines.&rdquo;</em></p>
<p>My other favorite podcasts are the news ones that are toned as if you were having a beer with the podcasters. They still relay information, but in a way that is so informal you barely realize that they are relaying some information that they spent their week learning. For politics, there is <strong><a href="http://www.npr.org/podcasts/510310/npr-politics-podcast" target="_blank" >NPR politics</a></strong> and often (nowadays) <strong><a href="https://www.thisamericanlife.org/podcast" target="_blank" >This American Life</a></strong>, for art there is <strong><a href="https://www.moma.org/calendar/exhibitions/3860" target="_blank" >A Piece of Work</a></strong>, for science there is <strong><a href="https://gimletmedia.com/science-vs/" target="_blank" >Science Vs.</a></strong>, and for music there is <strong><a href="http://www.npr.org/podcasts/510019/all-songs-considered" target="_blank" >All Songs Considered</a></strong> and for culture there is <strong><a href="http://www.npr.org/sections/monkeysee/129472378/pop-culture-happy-hour/" target="_blank" >Pop Culture Happy Hour</a></strong>. I love these podcasts, but I have to pace myself with them because the constant loose informality of the tone can become disingenuous and grating after awhile.</p>
<p>I have some favorite podcasts that are just funny or amusing to me, and
not entirely educational. These would include <strong><a href="http://www.whyohwhyradio.com/" target="_blank" >Why oh
why</a></strong> which accounts for dating in the
real world (though sometimes fictionally). Another great one is
<strong><a href="http://www.earwolf.com/show/beautiful-anonymous/" target="_blank" >Beautiful
Anonymous</a></strong> which is
sometimes about dating, but it is mostly about the real world. Then there
is just the improve humor of <strong><a href="http://www.maximumfun.org/shows/my-brother-my-brother-and-me" target="_blank" >My Brother My Brother and
Me</a></strong> which
I enjoy just to hear people crack themselves up.</p>
]]></description>
    </item>
    
    <item>
	    <title>Books I read (2017)</title>
	    <image>https://schollz.com/img/bisons.jpg</image>
      <link>/tinker/books-2017/</link>
      <pubDate>Sun, 10 Dec 2017 09:41:01 -0600</pubDate>
      
      <guid>/tinker/books-2017/</guid>
      <description><![CDATA[<p>I&rsquo;ve read about 15 books this year - not as many as I&rsquo;d like (<a href="/books-2008" >I read 40
in college</a>), but I did a lot of magazine reading too (e.g.
<em>The Sun</em> and <em>The New Yorker</em>). The books that I read can be classified
into three major categories: <em>non-fictional animal encounters</em>, <em>magical
horror realism</em> and <em>fictional non-fiction.</em></p>
<p>The first main category are books I read in 2017 are about <em>non-fictional animal encounters</em>. My favorite book in this category, and of 2017 overall, is <strong>Illumination in the Flat-woods</strong> by <em>Joe Hutto</em>. Its the real-like story of an experiment to raise turkey&rsquo;s through their entire life.
I never knew how amazing turkeys were until I read
this book. There is a movie by the same name that also is amazing, as they
were able to recapitulate a lot which occurred in the book (i.e. replicating the experiment). I can say, for
sure, that I think turkeys are amazing now. I will still eat turkey, but
now with a reverence I never had before.</p>
<p>I started out the year reading <strong>What a Fish Knows</strong> by <em>Jonathan
Balcombe</em>. I didn&rsquo;t quite finish this book - the first chapters a good but
it turns into a bit of a direct plea to save the fish instead of an
account of how amazing they are. An animal book shouldn&rsquo;t need to make
a direct plea</p>
<ul>
<li>the book should show enough how amazing the animals are and the reader
can decide for themselves, and its a hard decision to make if you learn
how awesome some of these animals are.</li>
</ul>
<p>Another good example of animal writing is <strong>Last Chance to See</strong> by
<em>Douglas Adams</em> is an interesting investigation of animals near
extinction, circa
1985. A lot of the animals in the book have since recovered, except the
Baiji dolphin, unfortunately. Despite the severe topic, its a funny and
entertaining book. Adams is a funny person and he gives some levity to
some of the dark topics. He is also a great everyman in the book who has
lots of things explained to him in a simple way - so its easy to pick up
information.</p>
<p>I&rsquo;ve been going out to see Bison every week now. I live in Edmonton, so
I&rsquo;m not far from Elk Island where there is a ton of wildish bison roaming
around. I&rsquo;ve literally run into them on the trail. Anyways, they are quite
impressive animals, and I wanted to learn more about them. I found a great
book, <strong>American Bison</strong> by <em>Steven Rinella</em>, which goes into their
history. A lot of the focus is on hunting, but hunting in a humane way
which is really interesting to learn about. Its a very respectful book
about the bison with lots of great information.</p>
<p>The next major category of book that I really enjoyed are books about
<em>magical horror realism</em>. I didn&rsquo;t realize this was a genre, but once
I found it I realized it was something that I really enjoyed. <strong>Library at
Mount Char</strong> by <em>Scott Hawkins</em> is my second-favorite book of
2017. The premise is bizarre, but the books twists in a much more bizarre
way than imaginable. This book is to fantasy what The Matrix is to
science fiction.</p>
<p>Another thrilling book I read was <strong>Weaveworld</strong> by <em>Clive Barker</em>, an
epic tale that goes back and forth between reality and fantasy until they
merge at the end. Great read. I actually found the previous book after
reading this book and trying to find another book that matched it, to
which I wrote <a href="https://rpiai.com/why-i-made-a-book-recommendation-service/" target="_blank" >an entire
program</a>
which ended up becoming a website for people:
<a href="https://www.booksuggestions.ninja" target="_blank" >booksuggestions.ninja</a>.</p>
<p>The third major category that I greatly enjoyed is of <em>ficiontal
non-fiction.</em> The first book I read of 2017 was <strong>Pieces of the Left
Hand</strong> by <em>J. Robert Lennon</em> and it is a little work of genius. They are
a collection of very, very short tales of a seemingly non-fictional
account of the author&rsquo;s life. The stories of very mundane things - stories
about the locals smashing mailboxes and dinner parties. Except they
inexplicably almost always take a weird and unbelievable end.</p>
<p>I have a soft spot for nonconformist writers like Bukowski and Hunter
Thompson. I found <strong>Gonzo Girl</strong> by <em>Cheryl Della Pietra</em> to be an
extraordinary fictional account of an assistant to Hunter Thompson. A lot
of it delves into tropes about genius (drug addition is a trope that it
seems to try to destroy) and sexism (which the author herself constantly
proclaims and then falls right into). Still, the writing is very good.</p>
<p><strong>Imaginary Imagnitude</strong> by <em>Stainslaw Lem</em> is a wholly amazing book</p>
<ul>
<li>from the philosophy of a self-aware computer system to the prologues of
books that have never existed (like a book about bacteria that
communicate). A lot of the Polish wordplay is lost in English, but it is
still funny nevertheless.</li>
</ul>
<p>Towards the end of the year I read <strong>The Three-Body Problem</strong> by <em>Liu Cixin</em> which I saw was recommended by former president Barack Obama. It gladdens my heart that this book in particular was recommended by Obama - its about the looming destruction of Earth by an Alien race which undermines humans by attempting to destroy their progress in <em>basic science</em>. The importance of basic science in this book makes me think that Obama may have had similar sentiments. Anways, the book is great - its translated well from Chinese and has lots of great information about the Chinese revolution and communism. The science explainations are not bad either.</p>
<h2 id="epistemological-articles">Epistemological articles</h2>
<p>One author has been sticking out to me this year</p>
<ul>
<li><em>Kathryn Schultz</em>. Whenever I read her articles I instantly realize its
her - she has an amazing knack for taking any kind of thesis and detailing
a complete and thoughtful story around it. She seems to like to take
strange thesis too. The first one I read was on <a href="https://www.newyorker.com/magazine/2015/10/19/pond-scum" target="_blank" >Henry Thoreau as
a hypocrite</a> (of
which I totally disagree but love the writing). My favorite from this year
were <strong><a href="https://www.newyorker.com/magazine/2017/10/16/how-to-be-a-know-it-all" target="_blank" >How to be
a know-it-all</a></strong>
which posits that its easy to learn a little bit about everything and
<strong><a href="https://www.newyorker.com/magazine/2017/11/06/is-bigfoot-likelier-than-the-loch-ness-monster" target="_blank" >Fantastic beasts and how to rank
them</a></strong>
which tries to understand why its possible to make judgements about things
that don&rsquo;t exist.</li>
</ul>
<h2 id="other-books">Other books</h2>
<ul>
<li><strong>Calculating God</strong> by <em>Robert J. Sawyer</em> (not finished)</li>
<li><strong>Hard Magic</strong> by <em>Larry Correia</em></li>
<li><strong>Death Comes for the Archbishop</strong> by <em>Willa Cather</em> (not finished)</li>
<li><strong>Everyone&rsquo;s An Aliebn when you&rsquo;re an Aliebn Too</strong> by <em>Jomny Sun</em></li>
<li><strong>Jack Reacher 61 hours</strong> by <em>Lee Child</em></li>
<li><strong>True Grit</strong> by <em>Charles Portis</em></li>
<li><strong>Leven Thumps and the Gateway to Foo</strong> by <em>Obert Skye</em></li>
<li><strong>Annihilation</strong> by <em>Jeff Vandermeer</em></li>
</ul>
]]></description>
    </item>
    
    <item>
	    <title>Painting from primitives</title>
	    <image>https://schollz.com/img/robin20171203.jpg</image>
      <link>/tinker/primitives/</link>
      <pubDate>Sun, 03 Dec 2017 18:25:57 -0700</pubDate>
      
      <guid>/tinker/primitives/</guid>
      <description><![CDATA[<p>Computers can do many amazing things with art, especially with the advent of neural networks.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> In 2016, I saw some computer-generated art that inspired me to generate art as a human. The art I saw was a rendition of photos in primitives. Here&rsquo;s an example (For much more, just <a href="https://twitter.com/PrimitivePic" target="_blank" >follow @primitivepic</a>):</p>
<p><a href="https://twitter.com/PrimitivePic/status/938243428130926593" target="_blank" ><img src="/img/bird_primitive.jpg" alt="200 triangles."></a></p>
<p>These kinds of photos were generated using a computer program that tries to replicate a photo by slowly building up primitive shapes (ellipses, rectangles, triangles) of a single color until eventually enough different shapes and colors converge on the original image. The program is available for free at <a href="https://github.com/fogleman/primitive" target="_blank" >github.com/fogleman/primitive</a>.</p>
<p>I played around with this program and soon found myself inspired and wanting to learn acrylic painting to paint in that style. I&rsquo;ve never painted in acrylic before (<a href="/watercolor" >previously I&rsquo;ve done only watercolor / gouache</a>),  but I thought I could learn do better than this computer program. So, in September 2016, I sought to become an artist using this computer program as the teacher.</p>
<p><img src="/img/bison_sergioboccardo_shutterstock3.jpg" alt="Stock image of Bison I chose to use"></p>
<p>I started out trying to take a photo and paint primitives. The photo I chose was of a bison.</p>
<p><img src="/img/bison_primitives.gif" alt="Primitive procedure generated"></p>
<p>I plugged this photo into the program and generated about 100 images that each added a single rectangle onto a base image. This was my guide, and all I had to do was follow the painting procedure from looking at the next image.</p>
<p><img src="/img/bison20160926.jpg" alt="“First Bison” - acrylic on canvas, September 2016"></p>
<p>My first painting follows exactly the primitive style using only rectangles. It remains one of my favorites though it is quite amateur. I misjudged a lot of the colors so everything became a lot brighter. I still struggle with creating subtle colors today, too.</p>
<h2 id="moving-towards-to-realism">Moving towards to realism</h2>
<p>I continued painting throughout the end of 2016 and now into 2017. I&rsquo;ve been painting mostly animals and lots of bison for some reason. One of my New Year Resolutions was to paint every week. I have been able to do that, mostly.</p>
<p>I learned a lot painting every week. I followed the advice of <a href="https://www.jessiedoyleart.com/" target="_blank" >Jessie Doyle</a> and learned to paint <em>what I see</em> instead of trying to paint <em>what I think I see</em>. I took a class with <a href="http://www.frankhaddock.com/Home.html" target="_blank" >Frank Haddock</a> and learned about how to subdue colors (just consider RGB and remove the current color and mix the remaining with white to make a subduing version) and how to use brushes (use filbert for lines).</p>
<p><img src="/img/bison20170701.jpg" alt="“Bison” - acrylic on canvas, July 2017"></p>
<p>I became enamored with the wet-on-wet acrylic because you could get blends of color and strokes. I also found that the primitives were too mechanical to get colors and I experimented with something more realistic.</p>
<h2 id="back-to-primitives">Back to primitives</h2>
<p>Now, at the end of 2017, I&rsquo;ve gone back to the using primitives, but without the restrictions on a particular shape of stroke. Instead, I focus on generating the painting in a few explicit strokes with a wide brush.</p>
<p>There is something wonderful about rendering the painting in a few strokes, in a primitive style. In principle it is simple - there are only a few strokes involved. However in practice it is complex - each stroke needs a color, a position and an orientation. There are many combinations for those three options and they have widely different aesthetic appeals. Suddenly determining the next stroke becomes difficult and possibly frusturating.  However, I find the result quite appealing.</p>
<p><img src="/img/bison20171202.jpg" alt="“Bison” - acrylic on canvas, December 2017"></p>
<p>Using the idea of primitives and improving upon my ability to draw and paint, I&rsquo;m now able to generate an acrylic painting that is much more satisfying to me in terms of primitives. For the record, I can also paint things other than bison, like birds<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>You&rsquo;ve read to the end of this! Wow. As a bonus, here are two painting tips that I would share as a still progressing novice:</p>
<ul>
<li>A nice trick I&rsquo;ve found is to <em>start with a red canvas</em> because it gives a great boldness to the underlying strokes that you don&rsquo;t get with a white canvas.</li>
<li>A lot of painting is overcoming frustration. Almost every painting here became extremely frustrating and infuriating to me at some point, but I was always able to work through and eventually like and love the result.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>For some neat examples about neural networks and art, check out my <a href="/watercolor/" >previous blog post</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><img src="/img/robin20171203.jpg" alt="“European Robin” - acrylic on canvas, December 2017">&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><img src="/img/finch20171201.jpg" alt="“Finch” - acrylic on canvas, December 2017">&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>makemydrivefun.com</title>
	    <image>https://schollz.com/img/projects/makemydrivefun.jpg</image>
      <link>/tinker/makemydrivefun/</link>
      <pubDate>Fri, 17 Nov 2017 08:00:46 -0700</pubDate>
      
      <guid>/tinker/makemydrivefun/</guid>
      <description><![CDATA[<p>TLDR: The website is hosted at <a href="https://makemydrivefun.com" target="_blank" >makemydrivefun.com</a>.</p>
<p>This is a web app that lets you easily find novelty places to stop at on a road trip - places that you wouldn&rsquo;t normally find in a travel book. I decided to make this when I was planning on moving and wanted to find some fun places to stop along the way of my planned route. I found <a href="https://www.google.com/maps/d/u/0/viewer?mid=1n9BYTMRAXcuawwWFeLr7Tqav2qc&amp;ll=38.66418226111179%2C-96.78450099999998&amp;z=2" target="_blank" >this map of places to checkout in Canada</a> which I used to get started, and then found some other places from Wikipedia and Google.</p>
<p>How does it work? At the top-level, it will generate a driving route between two cities and then it will attempt to find any of the 12,000 novelty features that are within 20 minutes driving distance of the route. These are then sorted, collated, and displayed on the web page.</p>
]]></description>
    </item>
    
    <item>
	    <title>Nesting templates in Golang</title>
	    <image>https://schollz.com/img/templates.svg</image>
      <link>/tinker/templates/</link>
      <pubDate>Fri, 10 Nov 2017 04:50:00 -0700</pubDate>
      
      <guid>/tinker/templates/</guid>
      <description><![CDATA[<p>This is a nice code snippet for understanding the Go template nesting. I refer to this time to time and I thought I&rsquo;d put it here in case anyone else needed help with it.</p>
<p><a href="https://play.golang.org/p/OVkruYsBVV" target="_blank" >https://play.golang.org/p/OVkruYsBVV</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl">    <span class="s">&#34;bytes&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl">    <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl">    <span class="s">&#34;log&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl">    <span class="s">&#34;text/template&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl"><span class="kd">type</span> <span class="nx">View</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl">    <span class="nx">Title</span>   <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl">    <span class="nx">Content</span> <span class="kt">string</span>
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl">    <span class="nx">header</span> <span class="o">:=</span> <span class="s">`
</span></span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl"><span class="s"></span><span class="cp">{{</span><span class="k">define</span><span class="w"> </span><span class="s">&#34;header&#34;</span><span class="cp">}}</span><span class="s">
</span></span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl"><span class="s">     &lt;head&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl"><span class="s">         &lt;title&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">$.Title</span><span class="w"> </span><span class="cp">}}</span><span class="s">&lt;/title&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl"><span class="s">     &lt;/head&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a></span><span class="cl"><span class="s"></span><span class="cp">{{</span><span class="k">end</span><span class="cp">}}</span><span class="s">`</span>
</span></span><span class="line"><span class="ln" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a></span><span class="cl">    <span class="nx">page</span> <span class="o">:=</span> <span class="s">`
</span></span></span><span class="line"><span class="ln" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a></span><span class="cl"><span class="s">This line should not show
</span></span></span><span class="line"><span class="ln" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a></span><span class="cl"><span class="s"></span><span class="cp">{{</span><span class="k">define</span><span class="w"> </span><span class="s">&#34;indexPage&#34;</span><span class="cp">}}</span><span class="s">
</span></span></span><span class="line"><span class="ln" id="hl-0-27"><a class="lnlinks" href="#hl-0-27">27</a></span><span class="cl"><span class="s">    &lt;html&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-28"><a class="lnlinks" href="#hl-0-28">28</a></span><span class="cl"><span class="s">    </span><span class="cp">{{</span><span class="k">template</span><span class="w"> </span><span class="s">&#34;header&#34;</span><span class="w"> </span><span class="na">.</span><span class="cp">}}</span><span class="s">
</span></span></span><span class="line"><span class="ln" id="hl-0-29"><a class="lnlinks" href="#hl-0-29">29</a></span><span class="cl"><span class="s">    &lt;body&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-30"><a class="lnlinks" href="#hl-0-30">30</a></span><span class="cl"><span class="s">        &lt;h1&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}</span><span class="s">&lt;/h1&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-31"><a class="lnlinks" href="#hl-0-31">31</a></span><span class="cl"><span class="s">    &lt;/body&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-32"><a class="lnlinks" href="#hl-0-32">32</a></span><span class="cl"><span class="s">    &lt;/html&gt;
</span></span></span><span class="line"><span class="ln" id="hl-0-33"><a class="lnlinks" href="#hl-0-33">33</a></span><span class="cl"><span class="s"></span><span class="cp">{{</span><span class="k">end</span><span class="cp">}}</span><span class="s">`</span>
</span></span><span class="line"><span class="ln" id="hl-0-34"><a class="lnlinks" href="#hl-0-34">34</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-35"><a class="lnlinks" href="#hl-0-35">35</a></span><span class="cl">    <span class="nx">view</span> <span class="o">:=</span> <span class="nx">View</span><span class="p">{</span><span class="nx">Title</span><span class="p">:</span> <span class="s">&#34;some title&#34;</span><span class="p">,</span> <span class="nx">Content</span><span class="p">:</span> <span class="s">&#34;some content&#34;</span><span class="p">}</span> <span class="c1">// Here we try to set which page to view as content
</span></span></span><span class="line"><span class="ln" id="hl-0-36"><a class="lnlinks" href="#hl-0-36">36</a></span><span class="cl"><span class="c1"></span>    <span class="nx">t</span> <span class="o">:=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;basic&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-37"><a class="lnlinks" href="#hl-0-37">37</a></span><span class="cl">    <span class="nx">t</span> <span class="p">=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">Must</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">header</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-0-38"><a class="lnlinks" href="#hl-0-38">38</a></span><span class="cl">    <span class="nx">t</span> <span class="p">=</span> <span class="nx">template</span><span class="p">.</span><span class="nf">Must</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">page</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-0-39"><a class="lnlinks" href="#hl-0-39">39</a></span><span class="cl">    <span class="kd">var</span> <span class="nx">tpl</span> <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span>
</span></span><span class="line"><span class="ln" id="hl-0-40"><a class="lnlinks" href="#hl-0-40">40</a></span><span class="cl">    <span class="nx">err</span> <span class="o">:=</span> <span class="nx">t</span><span class="p">.</span><span class="nf">ExecuteTemplate</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">tpl</span><span class="p">,</span> <span class="s">&#34;indexPage&#34;</span><span class="p">,</span> <span class="nx">view</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-41"><a class="lnlinks" href="#hl-0-41">41</a></span><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-0-42"><a class="lnlinks" href="#hl-0-42">42</a></span><span class="cl">        <span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;executing template:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-43"><a class="lnlinks" href="#hl-0-43">43</a></span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-0-44"><a class="lnlinks" href="#hl-0-44">44</a></span><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">tpl</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="ln" id="hl-0-45"><a class="lnlinks" href="#hl-0-45">45</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div>]]></description>
    </item>
    
    <item>
	    <title>My bike was stolen</title>
	    <image>https://schollz.com/img/biker.jpg</image>
      <link>/tinker/bike/</link>
      <pubDate>Sat, 28 Oct 2017 04:31:13 -0600</pubDate>
      
      <guid>/tinker/bike/</guid>
      <description><![CDATA[<p>On Wednesday, after work, in 2010, I went to get my bike to ride home. My brand new gray-and-red racing bike. It had bright red tires and red bar-tape. It looked and fast and it was fast. But, I glanced to where my bike should be and noticed my bike was gone. There was a piece of Velcro from my bike lock in the place my bike had been. <em>Stolen</em>, I thought. The Velcro could only come off if the bike lock had been cut.  I called the police. They weren&rsquo;t interested. Lots of bikes go missing and they are impossible to get back, they said.</p>
<p>I ran home. I got onto my computer. Maybe my bike would be on Craigslist? Probably not. I looked, and sure enough, I found my bike for sale on Craigslist!  I called the police again and told them the good news. Unfortunately, they told me the bike is most certainly not mine and that it was not worth pursuing. I told them I had the original receipt for the bike with the serial number, and it would take only a second to compare it to the stolen bike. The police still were not interested. I went for another run to clear my mind, but I couldn&rsquo;t stop thinking about how to get my bike back. I decided to act, on my own.</p>
<p>I went to Walmart and bought a burn phone.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>  While I was in the checkout line I made some fake personas complete with backstories and email addresses - James Hannover and Mathew Phillipe. My backstory for James Hannover was a that he was a relatively new student to NCSU who works at a pizzeria and is interested in doing triathlons. For Mathew Phillipe the backstory was that he was a Durhamite who loved soccer and was looking to race his friend on Tobacco Road.</p>
<p>I emailed the thief from both addresses and got replies to both!<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> I chose to use my fake handle James to get a meeting scheduled for Thursday and gave the thief the phone number for my new burn phone. Right after scheduling, the thief emailed my other handle, Mathew Phillipe, saying he already had a buyer<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> (which was also me). I was going to steal the bike back.</p>
<p>On Thursday, I called the police to tell them my plan to meet the thief and bike away with my bike. They said not to do it. They said they could help, but not right away. Given the police&rsquo;s lack of enthusiasm the first time, I ignored this, and went ahead with the plan. I called the thief and told him I was stuck at work<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> but could pick the bike up on Friday. The thief wanted to meet Thursday, but he reluctantly agreed to Friday.</p>
<p>On Friday, the thief texted me to confirm the time and place. I called the police one more time, asking if they would help. The police were surprised at the level of investigation and they reluctantly agreed to help now.  They wanted to have me setup a meeting with the thief right by the police station. <em>That is silly</em>, I thought, <em>the thief would catch on to that</em>.  I told the police my back story: that I, known to the thief as James, was supposedly a new student to NCSU who worked in a pizzeria and who wasn&rsquo;t familiar with the area. The police seemed to agree that the new location would be a give-away. I suggested instead the front of James Duke statue in the center of campus and they agreed.  Now I didn&rsquo;t have to steal my bike back. I had setup a police sting to do it for me.</p>
<p>The hour of the sting arrived. The police called me and asked me when to get into position. As I was on the phone with the police, the thief called.  I told him that we should meet next to the statue instead, because I had a friend at Duke that I was meeting afterwards. I asked what he looked like. He said blue shirt and shorts. I said I was on 40 heading there and would be there in 20 minutes. The police were still on hold, so I updated them.  They said they&rsquo;d be there, but probably not with many cops. I quickly ran over to watch.</p>
<p>I got to the location of the sting right as it was happening. Despite what the police investigator said, they sent the entire police force. Three police cars and five police men were surrounding this little skinny white kid and my bike. The main investigator called me over to look at the bike as they were cuffing the thief.  I did, confirming it was certainly my bike, right down to the campus pedals and the red light I got for Hanukkah, and correct serial number.  She told me to take the bike down to the police station, which I did.  I waited there and watched the kid get booked in.  He didn&rsquo;t look happy behind his retro glasses.  I talked to the policemen, told them about the fake identifies and the burn phone. The main investigator was happy.  She said she discourages people from conducting their own investigations but that I did a good job. She said this was the first time she got a bike that had been posted to Craigslist back to the original owner.</p>
<p>Nine months later, I got a call from the Durham District Attorney. It turns out that the thief had some drug problems and pleaded guilty to a count of larceny, possession of stolen goods, and trespassing on private property.  He went to jail for 30 days and then got out on parole for 120 days and entered a drug rehab program. I&rsquo;ve been riding the bike ever since.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>A TracFone with prepaid plan -  $15 for 60 minutes.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Reply from Thief: &ldquo;<em>you said you were doing tris soon? That awesome if you&rsquo;d like I could throw in a pair of Ultegra 6700 pedals for an even 600.</em>&rdquo; At least the price was decent.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Reply from Thief: &ldquo;<em>I have someone coming to look at it is who will most likely buy it on spot.</em>&rdquo;&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Luckily, in my backstory, James Hannover works at a pizzeria which has odd hours.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>I ended up selling this bike when I moved away, though I got a good five years with it!&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Progress bar</title>
	    <image>https://schollz.com/img/gopherprogress.jpg</image>
      <link>/tinker/progressbar/</link>
      <pubDate>Thu, 26 Oct 2017 07:50:07 -0700</pubDate>
      
      <guid>/tinker/progressbar/</guid>
      <description><![CDATA[<p>A very simple thread-safe progress bar which should work on every OS without problems. I needed a progressbar for <a href="https://github.com/schollz/croc" target="_blank" >croc</a> and everything I tried had problems, so I made another one. In order to be OS agnostic I do not plan to support <a href="https://github.com/schollz/progressbar/issues/6" target="_blank" >multi-line outputs</a>.</p>
<h2 id="install">Install</h2>
<pre tabindex="0"><code>go get -u github.com/schollz/progressbar/v3
</code></pre><h2 id="usage">Usage</h2>
<h3 id="basic-usage">Basic usage</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="nx">bar</span> <span class="o">:=</span> <span class="nx">progressbar</span><span class="p">.</span><span class="nf">Default</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl"><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl">    <span class="nx">bar</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a></span><span class="cl">    <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">40</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5">5</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>which looks like:</p>
<p><img src="https://raw.githubusercontent.com/schollz/progressbar/master/examples/basic/basic.gif" alt="Example of basic bar"></p>
<h3 id="io-operations">I/O operations</h3>
<p>The <code>progressbar</code> implements an <code>io.Writer</code> so it can automatically detect the number of bytes written to a stream, so you can use it as a progressbar for an <code>io.Reader</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">,</span> <span class="s">&#34;https://dl.google.com/go/go1.14.2.src.tar.gz&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl"><span class="nx">resp</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">DefaultClient</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl"><span class="k">defer</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl"><span class="nx">f</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">OpenFile</span><span class="p">(</span><span class="s">&#34;go1.14.2.src.tar.gz&#34;</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">O_CREATE</span><span class="p">|</span><span class="nx">os</span><span class="p">.</span><span class="nx">O_WRONLY</span><span class="p">,</span> <span class="mo">0644</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl"><span class="k">defer</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl"><span class="nx">bar</span> <span class="o">:=</span> <span class="nx">progressbar</span><span class="p">.</span><span class="nf">DefaultBytes</span><span class="p">(</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">    <span class="nx">resp</span><span class="p">.</span><span class="nx">ContentLength</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl">    <span class="s">&#34;downloading&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl"><span class="nx">io</span><span class="p">.</span><span class="nf">Copy</span><span class="p">(</span><span class="nx">io</span><span class="p">.</span><span class="nf">MultiWriter</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span> <span class="nx">bar</span><span class="p">),</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span></code></pre></div><p>which looks like:</p>
<p><img src="https://raw.githubusercontent.com/schollz/progressbar/master/examples/download/download.gif" alt="Example of download bar"></p>
<h3 id="progress-bar-with-unknown-length">Progress bar with unknown length</h3>
<p>A progressbar with unknown length is a spinner. Any bar with -1 length will automatically convert it to a spinner with a customizable spinner type. For example, the above code can be run and set the <code>resp.ContentLength</code> to <code>-1</code>.</p>
<p>which looks like:</p>
<p><img src="https://raw.githubusercontent.com/schollz/progressbar/master/examples/download-unknown/download-unknown.gif" alt="Example of download bar with unknown length"></p>
<h3 id="customization">Customization</h3>
<p>There is a lot of customization that you can do - change the writer, the color, the width, description, theme, etc. See <a href="https://pkg.go.dev/github.com/schollz/progressbar/v3?tab=doc#Option" target="_blank" >all the options</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a></span><span class="cl"><span class="nx">bar</span> <span class="o">:=</span> <span class="nx">progressbar</span><span class="p">.</span><span class="nf">NewOptions</span><span class="p">(</span><span class="mi">1000</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a></span><span class="cl">    <span class="nx">progressbar</span><span class="p">.</span><span class="nf">OptionSetWriter</span><span class="p">(</span><span class="nx">ansi</span><span class="p">.</span><span class="nf">NewAnsiStdout</span><span class="p">()),</span>
</span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a></span><span class="cl">    <span class="nx">progressbar</span><span class="p">.</span><span class="nf">OptionEnableColorCodes</span><span class="p">(</span><span class="kc">true</span><span class="p">),</span>
</span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a></span><span class="cl">    <span class="nx">progressbar</span><span class="p">.</span><span class="nf">OptionShowBytes</span><span class="p">(</span><span class="kc">true</span><span class="p">),</span>
</span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a></span><span class="cl">    <span class="nx">progressbar</span><span class="p">.</span><span class="nf">OptionSetWidth</span><span class="p">(</span><span class="mi">15</span><span class="p">),</span>
</span></span><span class="line"><span class="ln" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a></span><span class="cl">    <span class="nx">progressbar</span><span class="p">.</span><span class="nf">OptionSetDescription</span><span class="p">(</span><span class="s">&#34;[cyan][1/3][reset] Writing moshable file...&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="ln" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a></span><span class="cl">    <span class="nx">progressbar</span><span class="p">.</span><span class="nf">OptionSetTheme</span><span class="p">(</span><span class="nx">progressbar</span><span class="p">.</span><span class="nx">Theme</span><span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a></span><span class="cl">        <span class="nx">Saucer</span><span class="p">:</span>        <span class="s">&#34;[green]=[reset]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a></span><span class="cl">        <span class="nx">SaucerHead</span><span class="p">:</span>    <span class="s">&#34;[green]&gt;[reset]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a></span><span class="cl">        <span class="nx">SaucerPadding</span><span class="p">:</span> <span class="s">&#34; &#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a></span><span class="cl">        <span class="nx">BarStart</span><span class="p">:</span>      <span class="s">&#34;[&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a></span><span class="cl">        <span class="nx">BarEnd</span><span class="p">:</span>        <span class="s">&#34;]&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a></span><span class="cl">    <span class="p">}))</span>
</span></span><span class="line"><span class="ln" id="hl-3-14"><a class="lnlinks" href="#hl-3-14">14</a></span><span class="cl"><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-15"><a class="lnlinks" href="#hl-3-15">15</a></span><span class="cl">    <span class="nx">bar</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-16"><a class="lnlinks" href="#hl-3-16">16</a></span><span class="cl">    <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-17"><a class="lnlinks" href="#hl-3-17">17</a></span><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>which looks like:
<img src="https://raw.githubusercontent.com/schollz/progressbar/master/examples/customization/customization.gif" alt="Example of customized bar"></p>
<h2 id="contributing">Contributing</h2>
<p>Pull requests are welcome. Feel free to&hellip;</p>
<ul>
<li>Revise documentation</li>
<li>Add new features</li>
<li>Fix bugs</li>
<li>Suggest improvements</li>
</ul>
<h2 id="thanks">Thanks</h2>
<p>Thanks <a href="https://github.com/dynom" target="_blank" >@Dynom</a> for massive improvements in version 2.0!</p>
<p>Thanks <a href="https://github.com/CrushedPixel" target="_blank" >@CrushedPixel</a> for adding descriptions and color code support!</p>
<p>Thanks <a href="https://github.com/MrMe42" target="_blank" >@MrMe42</a> for adding some minor features!</p>
<p>Thanks <a href="https://github.com/tehstun" target="_blank" >@tehstun</a> for some great PRs!</p>
<p>Thanks <a href="https://github.com/Benzammour" target="_blank" >@Benzammour</a> and <a href="https://github.com/haseth" target="_blank" >@haseth</a> for helping create v3!</p>
<p>Thanks <a href="https://github.com/briandowns" target="_blank" >@briandowns</a> for compiling the list of spinners.</p>
]]></description>
    </item>
    
    <item>
	    <title>A tool for web scraping.</title>
	    <image>https://schollz.comhttps://user-images.githubusercontent.com/6550035/31456157-58663efe-ae76-11e7-8e53-6a2a5b7a196c.png</image>
      <link>/tinker/crawdad/</link>
      <pubDate>Wed, 11 Oct 2017 20:03:10 -0600</pubDate>
      
      <guid>/tinker/crawdad/</guid>
      <description><![CDATA[<p><em>crawdad</em> is a simple, yet powerful alternative for scraping in a distributed, persistent manner (backed by Redis). It can  do simple things, like generating site maps. It can also do complicated things, like extracting all the quotes from every page on a quotes website (tutorial follows).</p>
<h2 id="install">Install</h2>
<p>First <a href="https://www.docker.com/community-edition" target="_blank" >get Docker</a> which will be used for running Redis.</p>
<p>Then you can simply download <em>crawdad</em>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl">$ wget https://github.com/schollz/crawdad/releases/download/v3.0.0/crawdad_3.0.0_linux_amd64.zip
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl">$ unzip crawdad*zip
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3">3</a></span><span class="cl">$ sudo mv crawdad*amd64 /usr/local/bin/crawdad
</span></span></code></pre></div><p>Unlike many other scraping frameworks, <em>crawdad</em> is a single binary that has no dependencies.</p>
<h2 id="configure">Configure</h2>
<p>For scraping, <em>crawdad</em> requires creating a <a href="https://github.com/schollz/pluck#use-config-file" target="_blank" >pluck configuration file</a>. Here is the configuration file for scraping <a href="http://quotes.toscrape.com" target="_blank" >quotes.toscrape.com</a>:</p>
<script src="https://gist.github.com/schollz/02205b5c1a3c5ade132e17ce61ce1213.js"></script>
<p><em>pluck</em> is a language-agnostic way of extracting structured data from text without HTML/CSS/Regex. Essentially <em>pluck</em> is configured in a way you would tell your friend to grab data.</p>
<p>For example, the first <em>pluck</em> unit describes how you would get the quote text from <a href="http://quotes.toscrape.com" target="_blank" >quotes.toscrape.com</a>. Starting from the beginning of the source, you look for the string &ldquo;<code>span class=&quot;text&quot;</code>&rdquo; (called an <em>activator</em>). Once that is found, you look for a &ldquo;<code>&gt;</code>&rdquo;, the next activator. Then you capture all the characters until a &ldquo;<code>&lt;</code>&rdquo; is seen (the <em>deactivator</em>). This will allow you to collect all the quotes.</p>
<p>Each of the <em>pluck</em> units will be found simultaneously and extracted from any HTML page crawled by <em>crawdad</em>.</p>
<h2 id="run">Run</h2>
<p>First, start Redis with Docker:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl">$ docker run -d -p 6374:6379 redis
</span></span></code></pre></div><p>and then start <em>crawdad</em>:</p>
<pre tabindex="0"><code>$ time crawdad -p 6374 -set -u http://quotes.toscrape.com -pluck quotes.toml -include &#39;/page/&#39; -exclude &#39;/tag/&#39;
0.12s user 0.03s system 5% cpu 2.666 total
</code></pre><p>The <code>-set</code> flag tells the <em>crawdad</em> to create some new settings with a URL (<code>-u</code>) and a <em>pluck</em> configuration (<code>-pluck</code>) and with some inclusions/exclusions (<code>-include</code>/<code>-exclude</code>). The inclusions and exclusions ensures that only the <code>/page</code> links will be followed (in order to compare with scrapy).</p>
<h2 id="extract-data">Extract data</h2>
<p>The data from <em>crawdad</em> can be parsed in the same as <em>scrapy</em> by first dumping it,</p>
<pre tabindex="0"><code>$ crawdad -p 6374 -done done.json
</code></pre><p>The data, <code>done.json</code>, contains each URL as a key and the data it extracted. It needs to be quickly parsed, too, which can be done lickity-split in Python in 12 lines of code:</p>
<script src="https://gist.github.com/schollz/f27547bb4716fc14fd574e9bbdad57a1.js"></script>
<h2 id="crawdad-bonuses"><em>crawdad</em> bonuses</h2>
<p><em>crawdad</em> has some other mighty benefits as well. Once initiated, you can run another crawdad on a different machine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1">1</a></span><span class="cl">$ crawdad -p <span class="m">6374</span> -s X.X.X.X
</span></span></code></pre></div><p>This will start crawling using the same parameters as the first <em>crawdad</em>, but will pull from the queue. Thus, you can easily make a distributed crawler.</p>
<p>Also, since it is backed by Redis, <em>crawdad</em> is resilient to interruptions and allows you to restart from the point that it was interrupted. Try it!</p>
<h1 id="comparison-with-scrapy">Comparison with <em>scrapy</em></h1>
<p>Here I will compare scraping the same site, <a href="http://quotes.toscrape.com/" target="_blank" >quotes.toscrape.com</a> with <em>crawdad</em> (my creation) and <em>scrapy</em> (the popular framework for scraping).</p>
<p><img src="https://user-images.githubusercontent.com/6550035/31486741-b06865e4-aef5-11e7-8b0d-c5ed107b25b4.png" alt="scrapy is really useful tool to get started in scraping."></p>
<p><em>scrapy</em> is powerful, but complicated. Lets follow the tutorial to get a baseline on how a scrapy should run.</p>
<h2 id="install-1">Install</h2>
<p>First install <em>scrapy</em> by installing the dependencies (there are a lot of dependencies).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="ln" id="hl-5-1"><a class="lnlinks" href="#hl-5-1">1</a></span><span class="cl">$ sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
</span></span><span class="line"><span class="ln" id="hl-5-2"><a class="lnlinks" href="#hl-5-2">2</a></span><span class="cl">$ sudo -H python3 -m pip install --upgrade scrapy
</span></span></code></pre></div><p>Once you get it install you can check the version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="ln" id="hl-6-1"><a class="lnlinks" href="#hl-6-1">1</a></span><span class="cl">$ scrapy -version
</span></span><span class="line"><span class="ln" id="hl-6-2"><a class="lnlinks" href="#hl-6-2">2</a></span><span class="cl">Scrapy 1.4.0 - project: quotesbot
</span></span></code></pre></div><h2 id="configure-1">Configure</h2>
<p>Actually, I will just use the <a href="https://github.com/scrapy/quotesbot" target="_blank" >tutorial of <em>scrapy</em></a> to skip building it myself.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="ln" id="hl-7-1"><a class="lnlinks" href="#hl-7-1">1</a></span><span class="cl">$ git clone https://github.com/scrapy/quotesbot.git
</span></span><span class="line"><span class="ln" id="hl-7-2"><a class="lnlinks" href="#hl-7-2">2</a></span><span class="cl">$ <span class="nb">cd</span> quotesbot
</span></span></code></pre></div><p><em>scrapy</em> is not simple. It requires &gt; 40 lines of Python code in several different files (<code>items.py</code>, <code>pipelines.py</code>, <code>settings.py</code>, <code>spiders/toscrape-css.py</code>).</p>
<h2 id="run-1">Run</h2>
<p>Lets run and time the result:</p>
<pre tabindex="0"><code>$ time scrapy crawl toscrape-xpath -o quotes.json
1.06s user 0.08s system 29% cpu 3.877 total
</code></pre><p><em>scrapy</em> is about 10-30% slower than <em>crawdad</em>, plus it can not easily be run in a distributed, persistent way.</p>
]]></description>
    </item>
    
    <item>
	    <title>Calculating how to cool coffee</title>
	    <image>https://schollz.com/img/ice.png</image>
      <link>/tinker/ice-cubes/</link>
      <pubDate>Sun, 24 Sep 2017 09:43:35 -0600</pubDate>
      
      <guid>/tinker/ice-cubes/</guid>
      <description><![CDATA[<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  tex2jax: {
    inlineMath: [['$','$'], ['\\(','\\)']],
    displayMath: [['$$','$$'], ['\[','\]']],
    processEscapes: true,
    processEnvironments: true,
    "HTML-CSS": { 
         linebreaks: { automatic: true }
    },
    SVG: { 
         linebreaks: { automatic: true } 
    },
    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
    TeX: { equationNumbers: { autoNumber: "AMS" },
         extensions: ["AMSmath.js", "AMSsymbols.js"] }
  }
});
</script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Queue(function() {
    // Fix <code> tags after MathJax finishes running. This is a
    // hack to overcome a shortcoming of Markdown. Discussion at
    // https://github.com/mojombo/jekyll/issues/199
    var all = MathJax.Hub.getAllJax(), i;
    for(i = 0; i < all.length; i += 1) {
        all[i].SourceElement().parentNode.className += ' has-jax';
    }
});
</script>
<p>How many ice cubes do you need to <em>quickly</em> cool down a hot beverage to its perfect drinking temperature?</p>
<h3 id="the-beverage">The beverage</h3>
<p>The hot beverage must lose a certain amount of energy in order to decrease its temperature to a warm temperature. The energy of heat loss is given by a difference of temperature. The equation is</p>
<p>
$$Q_{beverage} = c_{water} \times m_{beverage} \times \Delta T_{beverage}$$
</p>
<p>
The specific heat of water, $c_{water}$ is 4.2 $J / g / °C$. The mass of a typical cup of coffee or tea, $m_{beverage}$  is typically around 480 grams. The change in temperature, $\Delta T_{beverage}$, is the change between the initial brewing temperature and the final drinking temperature.
</p>
<p>
$$Q_{beverage} = c_{water} \times m_{beverage} \times (T_{i}-T_{f})$$
</p>
<h3 id="the-ice">The ice</h3>
<p>Ice melts by absorbing heat, or energy. The heat absorbed by ice is given by</p>
<p>$$ Q_{ice} = m_{ice} \times Q_{fusion}$$</p>
<p>
where $Q_{fusion}$ is the enthalpy of fusion for ice, which is 334 $J/g$. Enthalpy of fusion is simply the amount of energy that will be needed to transform ice into cold water.
</p>
<p>Once the ice changes into cold water, it will begin to warm up, by absorbing heat from the beverage. As before, the water will absorb according to the equation</p>
<p>
$$Q_{ice-water} = c_{water} \times m_{ice} \times \Delta T_{ice}$$ 
</p>
<p>
In this case, the change in temperature, $\Delta T_{ice}$, is simply the final temperature, $T_{f}$, since ice has a temperature of 0°C.
</p>
<p>
$$Q_{ice-water} = c_{water} \times m_{ice} \times T_{f}$$ 
</p>
<h3 id="the-beverage--ice">The beverage + ice</h3>
<p>
When we reach equilibrium, it means that the total energy lost by the beverage equals the sum of the energy of the ice and the cold water (once the ice has transformed), $Q_{beverage}= Q_{ice-water} + Q_{ice}$. When we plug in the equations above we get
</p>
$$
c_{water} \times m_{beverage} \times (T_{i}-T_{f}) = m_{ice} \times Q_{fusion} \\
+ c_{water} \times m_{ice} \times T_{i}
$$
<p>
The only unknown is the mass of the ice, $m_{ice}$, which we can solve for to get
</p>
<p>
$$
m_{ice} = \frac{c_{water} \times m_{beverage} \times (T_{i}-T_{f})}{Q_{fusion} + c_{water} \times T_{i}}
$$
</p>
<h3 id="how-many-ice-cubes-do-you-need">How many ice cubes do you need?</h3>
<p>Coffee is initially at a temperature of 85°C. Tea has a similar high, undrinkable, temperature. The perfect drinking temperature is 50°C.  Given a typical cup of tea is about 480 grams, <a href="http://www.wolframalpha.com/input/?i=((4.2+*+480+*+(85-50)+%2F+(334+%2B4.2*50))+%2F+(30)"><strong>you need about 4 regular sized ice cubes.</strong></a> (A regular ice cube from a ice tray is about 30 g).</p>
<h1 id="how-fast-does-an-ice-cube-melt">How fast does an ice cube melt?</h1>
<p>How much times does it take for the ice to melt? There are several modes of heat transfer - conduction, convection, radiation. It turns out that ice is pretty well described by mainly the convection heat transfer. This is described by <a href="https://en.wikipedia.org/wiki/Thermal_conduction#Differential_form" target="_blank" >Fourier&rsquo;s law of thermal conduction</a>:</p>
<p>
$$  \dot{Q}_{melt} = k A \frac{d T}{d x} $$
</p>
<p>where $\dot{Q}_{melt}$ is the heat flux and $k$ is the thermal conductivity. For water, the thermal conductivity is 0.591 $W / m / K$.</p>
<h3 id="ice-cube-area">Ice cube area</h3>
<p>The area of the ice cube can be approximated by a cube of the same mass. That is,</p>
<p>
$$ A =  6 \times (m_{ice})^{2/3} / 100^2 $$
</p>
<p>
where the mass of each one is $m_{ice}$ and the $100^2$ is for converting $cm^2$ to $m^2$. Though, really the surface area of the ice cubes might depends on time (the ice cube grows smaller).
</p>
<h3 id="temperature-gradient">Temperature gradient</h3>
<p>The gradient of the temperature is between the surface of the ice and the rest of the hot water. Though this gradient can be pretty complicated (the heat diffusion occurs), we can approximate from the literature. Here is an infrared image of ice melting at room temperature<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<p><img src="/img/ice.png" alt="Ice melting infrared"></p>
<p>As you can see, the ice-air boundary is typically 0.002 meters, so we can approximate the ice water-boundary as $dx = 0.002$. The temperature difference is just the difference between the water and the ice, which is at 0, so $dT = T_{i}$.</p>
<h3 id="how-long-does-it-take-to-melt">How long does it take to melt?</h3>
<p>
Finally, then to get the time we can just use the equation from before and divide:
$$ t_{melt} = Q_{ice} / \dot{Q}_{melt} $$
</p>
<p>so that</p>
<p>
$$ t_{melt}  = \frac{m_{ice} \times H_{fusion}}{k \times T_{i} \times 6 \times m_{ice}^{2/3} / 100^2 / dx } $$
</p>
<p>which is measured in seconds.</p>
<p>A typical ice cube weighs about 30 grams and a typical initial temperature for heated water is about 85°C. So <strong><a href="http://www.wolframalpha.com/input/?i=(30+*+334)+%2F+(0.591*+85+*+6+*+30%5E(2%2F3)%2F100%5E2+%2F+0.002))+seconds">a typical ice cube melts in about 70 seconds</a></strong>.</p>
<h1 id="theory-versus-experiment">Theory <em>versus</em> Experiment</h1>
<p>I hooked up an Arduino to a DS18B20 temperature probe and measured the change in temperature over time of 480 grams of water, with (red line) and without (blue line) ice.</p>
<p><img src="/img/ice_graph.png" alt="Temperature change over time"></p>
<p>The ice melted in about 65 seconds and brought the temperature down to 50.8°C. This is in good agreement to the theoretical time to melt of 70 seconds and the theoretical final temperature of 50°C.</p>
<p>In comparison, the water without out that started at about 77°C, without ice, took over 15 minutes to get to the same temperature!</p>
<h1 id="references">References</h1>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Badarch, Ayurzana. (2017). Application of macro and mesoscopic numerical models to hydraulic problems with solid substances. 10.13140/RG.2.2.27837.36325.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Raspberry Pi as piano accompaniment.</title>
	    <image>https://schollz.com/img/projects/pianoai.jpg</image>
      <link>/tinker/piano-ai/</link>
      <pubDate>Mon, 04 Sep 2017 07:22:50 -0700</pubDate>
      
      <guid>/tinker/piano-ai/</guid>
      <description><![CDATA[<p>I was inspired by <a href="http://www.npr.org/2017/07/24/538677517/fascinating-algorithm-dan-tepfers-player-piano-is-his-composing-partner" target="_blank" >Dan Tepfer&rsquo;s piano worlds</a> to explore my own universe of augmented piano playing. Could I write a program that learns <em>in realtime</em> to improvise with my style in the music breaks between my own playing? 🤖🎹</p>
<p>This is pretty much a play-by-play on making <em>PianoAI</em> in 8 days. If you just want to try out <em>PianoAI</em>, <a href="https://github.com/schollz/PIanoAI" target="_blank" >go to the Github for instructions and downloading</a>. Otherwise, stay here to read about my many programming follies.</p>
<h1 id="inspiration">Inspiration</h1>
<p>After watching <a href="http://www.npr.org/2017/07/24/538677517/fascinating-algorithm-dan-tepfers-player-piano-is-his-composing-partner" target="_blank" >Dan Tepfers&rsquo;s video on NPR</a>, I did some freeze frames of his computer and found out he uses the <a href="https://processing.org/tutorials/" target="_blank" >Processing software</a> to connect up to his (very fancy) MIDI keyboard. I pretty easily managed to get together <a href="https://gist.github.com/schollz/f8ec8687e7de784aee6831fb2ca24078" target="_blank" >something very similar</a> with my own (not fancy) MIDI keyboard.</p>
<p><a href="http://www.npr.org/2017/07/24/538677517/fascinating-algorithm-dan-tepfers-player-piano-is-his-composing-partner" target="_blank" ><img src="/img/freeze-frame-1.PNG" alt="Freeze frame of Dan Tepfer showing his software for making automated music"></a></p>
<p>As explained in the video, Dan&rsquo;s augmented piano playing basically allows you to mirror or echo certain notes in specified patterns. A lot of his patterns seem to be song dependent. After thinking, I decided I was interested in something a little different. I wanted to a piano accompaniment that learns <em>in realtime</em> to emulate my style and improvise in the spaces between my own playing, on any song.</p>
<p>I&rsquo;m not the first to make a Piano AI. Google made the <a href="https://github.com/googlecreativelab/aiexperiments-ai-duet" target="_blank" >A.I. Duet</a> which is a great program. But I wanted to see if I could make an A.I. specifically tuned to my own style.</p>
<h1 id="a-journey-into-ai">A journey into AI</h1>
<p>My equipment is nothing special. I bought it used from some guy who decided he had to give up trying to learn how to play piano 😢. Pretty much any MIDI keyboard and MIDI adapter will do.</p>
<p>I generally need to do everything at least twice in order to get it right. Also I need to draw everything out clearly in order to actually understand what I&rsquo;m doing. My process for writing programs then is basically as follows:</p>
<ol>
<li>Draw it out on paper.</li>
<li>Program it in Python.</li>
<li>Start over.</li>
<li>Draw it out on paper, <em>again</em>.</li>
<li>Program it in Go.</li>
</ol>
<p><img src="/img/1504528218929-27b7f7a4-1482-479b-a884-fc21d6c38301.jpg" alt="First two pages of 12 for figuring out what I&rsquo;m doing"></p>
<p>Each time I draw out my idea on paper, it takes about three pieces of paper before my idea actually starts to take form and possibly work.</p>
<h2 id="programming-a-piano-ai-in-python">Programming a Piano AI in Python</h2>
<p>Once I had an idea of what I was doing, I implemented everything in <a href="https://github.com/schollz/pyplayerpiano" target="_blank" >a set of Python scripts</a>. These scripts are built on <a href="https://www.pygame.org/wiki/about" target="_blank" >pygame</a> which has great support for MIDI. The idea is fairly simple - there are two threads: a metronome and a listener. The listener just records notes played by the host. The metronome ticks along and plays any notes in the queue, or asks the AI to send some new notes if none are in the queue.</p>
<p>I made it somewhat pluggable, as you can do variations on the AI so it can be easily outfitted with different piano augmentations. There is an algorithm for simply echoing, one for playing notes within the chord structure (after it determines the chords), and one for generating piano runs from a <a href="https://en.wikipedia.org/wiki/Markov_chain" target="_blank" >Markov chain</a>. Here&rsquo;s a movie of me playing with the latter version of this algorithm (when my right hand moves <em>away</em> from the keyboard, the AI begins to play until I play again):</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/I4eh8bHc9r4?rel=0" frameborder="0" allowfullscreen>
</iframe>
<figcaption>My first piano-playing AI</figcaption><br>
<p>There were a couple of things I didn&rsquo;t like about this. First, its not very good. At best, the piano AI accompaniment sounds like a small child trying hard to emulate my own playing (I think there are a couple of reasons for this - basically not taking into account velocity data and transition times). Secondly, these python scripts did not work on a Raspberry Pi (the video was shot with me using Windows)! I don&rsquo;t know why. I had trouble on Python3.4, so I upgraded to 3.6. With Python3.6, I still had weird problems. <code>pygame.fastevent.post</code> worked but <code>pygame.fastevent.get</code> did not. I threw up my hands at this and found an alternative.</p>
<p>The alternative is to write this in Go. <a href="https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=go&amp;lang2=python3" target="_blank" >Go is notably faster than Python</a> - which is quite useful since this is a low-latency application. My ears discern discrepancies of &gt; 20 milliseconds, so I want to keep processing times down to a minimum. I found <a href="https://github.com/rakyll/portmidi" target="_blank" >a Go midi library</a> so porting was very viable.</p>
<h2 id="programming-a-piano-ai-in-go">Programming a Piano AI in Go</h2>
<p><img src="/img/gopher-1.svg" alt="Playing piano with Go"></p>
<p>I decided to simplify a little bit, and instead of making many modules with different algorithms, I would focus on the one I&rsquo;m most interested: <em>a program that learns in realtime to improvise in the spaces between my own playing.</em> I took out some more sheets of paper and began.</p>
<h3 id="day-1">Day 1</h3>
<p>Most of the code is about the same with my previous Python scripts. When writing in Go, I found that spawning threads is so much easier than in Python. Threads are all over this program. There are threads for listening to midi, threads for playing notes, threads for keeping track of notes. I was tempted to use the brand new <a href="https://golang.org/pkg/sync/#Map" target="_blank" >Go 1.9 sync.Map</a> in my threads, but realized I could leverage maps of maps which is beyond the <code>sync.Map</code> complexity. Still, I just made a map of maps that is very similar to another sync map store that I wrote (<a href="https://github.com/schollz/jsonstore" target="_blank" >schollz/jsonstore</a>).</p>
<p>I attempted to make everything classy (pun-intended) so I implemented components (midi, music, ai) as their own objects with their own functions. So far, the midi listening works great, and seems to responds very fast. I also implemented play back functions and they work too - this is pretty easy.</p>
<h3 id="day-2">Day 2</h3>
<p>Started by refactoring all the code into folders because I&rsquo;d like to reserve the <code>New</code> function for each of the objects. The objects have solidified - there is a AI for learning / generating licks, a Music object for the piano note models, a Piano object for communicating with midi, and a Player object for pulling everything together.</p>
<p>I spent a lot of time with pen and paper figuring out how the AI should work. I realized that there is more than one way to make a Markov chain out of Piano notes. Piano notes have four basic properties: <em>pitch</em>, <em>velocity</em>, <em>duration</em>, and <em>lag</em> (time to next note). The basic Markov chain for piano notes would be four different Markov chains, one for each of the properties. It would be illustrated as such:</p>
<div style="text-align:center;">
<img alt="Basic Markov chain properties" src="/img/download.png" width="200px;" style="max-width:200px;">
</div>
<p>Here the next pitch for the next note (P2) is determined from the pitch of the previous note (P1). Similar for Velocity (V1/V2), Duration (D1/D2) and Lag (L1/L2). The actual Markov chain simply enumerates the relative frequencies of occurrence of the value of each property and uses a random selector to pick one.</p>
<p>However, the piano properties are not necessarily independent: sometimes there is a relationship between the pitch and velocity, or the velocity and the duration of a note. To account for this I&rsquo;ve allowed for different couplings. You can couple properties to the current or the last value of any other property. Currently I&rsquo;m only allowing two couplings, because that&rsquo;s complicated enough. But in theory, you could couple the value of the next pitch to the previous pitch and velocity and duration and lag!</p>
<p>Once I had everything figured out, theoretically, I began to implement the AI. The AI is simply a Markov chain, so it determines a table of relative frequencies of note properties and has a function for computing them from the cumulative probabilities and a random number. At the end of the night, it works! Well, kinda but not really. Here&rsquo;s a silly video of me playing a lick to it and getting some AI piano runs:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Kv-tnh8ulFQ?rel=0?ecver=1" frameborder="0" allowfullscreen></iframe>
<figcaption>Example of the basic Markov chain for accompaniment</figcaption><br>
<p>Seems like there is more improvement to be made tomorrow!</p>
<h3 id="day-3">Day 3</h3>
<p>There is no improvement to be made that I can find.</p>
<p>But maybe the coupling I tried yesterday is not very good. The coupling I&rsquo;m most interested can be visualized as:</p>
<div style="text-align:center;">
<img alt="Basic Markov chain properties, with coupling" src="/img/download--2-.png" width="100px;" style="max-width:100px;">
</div>
<p>In this coupling, the previous pitch determines the next pitch. The next velocity is determined by the previous velocity <em>and</em> the current pitch. The current pitch also determines the duration. And the current duration determines the current lag. This needs to be evaluated in the correct order (pitch, duration, velocity, lag) and that&rsquo;s left up to to user cause I don&rsquo;t want to program in tree traversals.</p>
<p>Well, I tried this and it sounds pretty bad. I&rsquo;m massively disappointed in the results. I think I need to try a different machine learning. I&rsquo;m going to make my repo public now, maybe someone will find it and take over for me because I&rsquo;m not sure I will continue on it.</p>
<h3 id="day-4">Day 4</h3>
<p>I&rsquo;ve been thinking, and I&rsquo;m going to try a neural net. (commit <a href="https://github.com/schollz/PIanoAI/tree/b5931ff6467569b706946e7a932ab596f0af2a93" target="_blank" >b5931ff6</a>).</p>
<p>I just tried a neural net. It went badly, to say the least. I tried several variations too. I tried feeding in the notes as pairs, either each property individually or all the properties as a vector. This didn&rsquo;t sound good - the timings were way off and the notes were all over the place.</p>
<p>I also tried a neural net where I send the layout of the whole keyboard (commit <a href="https://github.com/schollz/PIanoAI/tree/20948dfbb974793b2bdfe977e684e68fa9d33449" target="_blank" >20948dfb</a>) and then the keyboard layout that it should transition into. It sounds complicated because I think it is and it didn&rsquo;t seem to work either.</p>
<p>The biggest problem I noticed with the neural net is that it is hard to get some randomness. I tried introducing a random column as a random vector but it just creates too many spurious notes. Once the AI piano lick begins, it seems to just get stuck in a local minimum and doesn&rsquo;t explore much anymore. I think in order to make the neural net work, I&rsquo;d have to do what Google does and try to Deep learn what &ldquo;melody&rdquo; is and what a &ldquo;chord&rdquo; is and what a &ldquo;piano&rdquo; is. Ugh.</p>
<h3 id="day-5">Day 5</h3>
<p>I give up.</p>
<p>I did give up. But then, I couldn&rsquo;t help but rethink the entire project while running in the forest.</p>
<p><img src="/img/forest-1.jpg" alt="Sometimes you just have to look at some trees."></p>
<p>What is an AI really? Is it just supposed to play with my level of intelligence? What is my level of intelligence when I play? When I think about my own intelligence, I realize: <em>I&rsquo;m not very intelligent</em>!</p>
<p>Yes, I&rsquo;m a simple piano player. I just play notes in scales that belong to the chords. I have some little riffs that I mix in when I feel like it. Actually, the more I think about it I realize that my piano improvisation is like linking up little riffs that I reuse or copy or splice over and over. So the Piano AI should do just that!</p>
<p><img src="/img/test.jpg" alt="Another, smaller, piece of paper for thinking."></p>
<p>I wrote down the idea on the requisite piece of paper and went home to program it. Basically this version of the AI was a Markovian scheme again but greater than first order (i.e. remembering more than just the last note). And the Markov transitions should link up larger segments of notes that are known to be riffs (i.e. based off my playing history). I implemented a new AI for this (commit <a href="https://github.com/schollz/PIanoAI/commit/bc96f51230c4008268f3bf8803d7d3fd2cc76b75" target="_blank" >bc96f512</a>) and tried it out.</p>
<h3 id="day-6">Day 6</h3>
<p>What the heck! Someone put my <a href="https://www.producthunt.com/posts/raspberry-pi-piano-player-ai" target="_blank" >Piano AI on Product Hunt</a> today. Oh boy, I&rsquo;m not done, but I&rsquo;m almost done so I hope no one tries it today.</p>
<p><img src="/img/Screenshot-from-2017-09-04-07-14-38.png" alt="I like being on ProductHunt, but I wish I had finished first"></p>
<p>With a fresh brain I found a number of problems that were actually pretty easy to fix. I fixed:</p>
<ul>
<li><a href="https://github.com/schollz/PIanoAI/commit/8df133441afe845acfd6f3af602e62f7f5755e7f" target="_blank" >a bug where the maps have notes at the wrong beats</a> (I still don&rsquo;t know how this happens ut I check for it now)</li>
<li><a href="https://github.com/schollz/PIanoAI/commit/f2964f2e0fa485c898048cc896ba22c17db7ef35" target="_blank" >a bug to prevent the AI from improvising while its improvising</a></li>
<li><a href="https://github.com/schollz/PIanoAI/commit/e01d2ac2b31fd2bef6d65175913427c808e77834" target="_blank" >a bug to fix concurrent access to a map</a></li>
<li><a href="https://github.com/schollz/PIanoAI/commit/7216cd9ecad6707fbfadb6b7f5e973e891b6a77a" target="_blank" >a bug to prevent improvisation while learning</a></li>
</ul>
<p>And I add command-line flags so its ready to go. And it actually works! Here&rsquo;s some videos of me teaching for about 30 seconds and then jamming:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/bvMW71BJofc?rel=0" frameborder="0" allowfullscreen>
</iframe>
<figcaption>Clip #1</figcaption><br>
<iframe width="560" height="315" src="https://www.youtube.com/embed/vF0uQax56a4?rel=0" frameborder="0" allowfullscreen>
</iframe>
<figcaption>Clip #2</figcaption><br>
<iframe width="560" height="315" src="https://www.youtube.com/embed/yYuBqUxZtp0?rel=0" frameborder="0" allowfullscreen>
</iframe>
<figcaption>Clip #3</figcaption><br>
<h3 id="day-8">Day 8</h3>
<p>Lots of great feedback on this! Here is <a href="https://news.ycombinator.com/item?id=15174263" target="_blank" >a Hacker News discussion</a> which is illuminating.</p>
<p>Notably:</p>
<ul>
<li>There is <a href="http://peterlangston.com/Papers/amc.pdf" target="_blank" >a great paper about musical composition by Peter Langston</a> which has a &ldquo;riffology&rdquo; algorithm that seems similar to mine.</li>
<li>Dan Tepfer <a href="https://news.ycombinator.com/item?id=15183402" target="_blank" >seems to be using SuperCollider for procedural music composition, not Processing</a>.</li>
<li>There are some <a href="https://github.com/ogmacorp/EOgmaNeo" target="_blank" >great sequence predictors</a> that could be used to make this better</li>
</ul>
<p>This is likely not the last day, but if you have more ideas or questions, let me know! Tweet <a href="https://twitter.com/yakczar" target="_blank" >@yakczar</a>.</p>
]]></description>
    </item>
    
    <item>
	    <title>Raspberry Pi as an aurora alarm.</title>
	    <image>https://schollz.com/img/aurora.JPG</image>
      <link>/tinker/aurora/</link>
      <pubDate>Sun, 03 Sep 2017 19:15:32 -0700</pubDate>
      
      <guid>/tinker/aurora/</guid>
      <description><![CDATA[<p><a href="https://en.wikipedia.org/wiki/Aurora" target="_blank" >Auroras</a> (or northern lights) are amazing &ndash; a beautiful display of ionization of the disturbed <a href="https://en.wikipedia.org/wiki/Magnetosphere" target="_blank" >magnetosphere</a>. Auroras often happen in the dark of the night and their occurrence is <strong>random</strong>. The best predictions usually only provide an hour&rsquo;s notice. To make sure I can get to see the latest Canadian auroras, I made a Raspberry Pi Aurora Alarm.</p>
<p>There is a simple way of determining if there is an Aurora in Canada, you just go to <a href="http://www.aurorawatch.ca" target="_blank" >www.aurorawatch.ca</a>. There you will see a graph like this:</p>
<p><img src="/img/AuroraWatch20170901.png" alt="When the bar is red, there may be an Aurora!"></p>
<p>From that graph you can see that the Aurora on September 1st, 2017 did not occur until about 11pm. And, even though I was asleep at the time, I was awaken to see the aurora!</p>
<p>My setup is very simple. I have a Raspberry Pi that probes the Aurora with a simple Python function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>
</span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="k">def</span> <span class="nf">percent_aurora_right_now</span><span class="p">():</span>
</span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl">    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;http://www.aurorawatch.ca/&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl">    <span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="s1">&#39;html.parser&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl">    <span class="n">percent_aurora</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl">    <span class="k">for</span> <span class="n">span</span> <span class="ow">in</span> <span class="n">soup</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl">        <span class="k">if</span> <span class="s1">&#39;%&#39;</span> <span class="o">==</span> <span class="n">span</span><span class="o">.</span><span class="n">text</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl">            <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl">                <span class="n">percent_aurora</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">span</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;%&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl">            <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl">                <span class="k">pass</span>
</span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl">    <span class="k">return</span> <span class="n">percent_aurora</span>
</span></span></code></pre></div><p>and then I have a <code>for</code>-loop that periodically checks if an Aurora has occurred:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1">1</a></span><span class="cl"><span class="kn">import</span> <span class="nn">time</span> 
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2">2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3">3</a></span><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4">4</a></span><span class="cl">    <span class="k">if</span> <span class="n">percent_aurora_right_now</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">70</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5">5</a></span><span class="cl">        <span class="n">alarm</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6">6</a></span><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>
</span></span></code></pre></div><p>The <code>alarm()</code> function may have to be changed for yourself. You can do whatever alarm is best &ndash; push notifications, ringing buzzers, etc. Since I am a heavy sleeper, sound usually doesn&rsquo;t wake me up so I made a light alarm. That is, my lights will periodically flash when the Aurora occurs:</p>
<p><img src="/img/ezgif-1-4c27556e85.gif" alt="My Aurora light alarm in action"></p>
<p>I have two PIR sensors at the side of my bed so I can also turn on/off the lights manually. For light automation I&rsquo;m using <a href="http://www2.meethue.com/en-us/p/hue-white/046677455286" target="_blank" >Philips Hue Lights</a>.</p>
<p>Here&rsquo;s another picture from the last aurora I saw:</p>
<p><img src="/img/DSC_0217.JPG" alt="An Aurora in the middle of the night in the middle of Alberta, CA"></p>
<p>Here&rsquo;s my full code: <a href="https://gist.github.com/schollz/a6922e7ba94afa9b183cc48715607230.js" target="_blank" >https://gist.github.com/schollz/a6922e7ba94afa9b183cc48715607230.js</a></p>
]]></description>
    </item>
    
    <item>
	    <title>Recursive recipes</title>
	    <image>https://schollz.com/img/projects/recursiverecipes.jpg</image>
      <link>/tinker/recursive-recipes/</link>
      <pubDate>Fri, 23 Jun 2017 19:19:15 -0700</pubDate>
      
      <guid>/tinker/recursive-recipes/</guid>
      <description><![CDATA[<p>What would happen if you made a recipe out of the ingredients for a recipe? What if you repeated this process over and over? I ended up doing this and generated the recipe that starts with the most basic ingredients - dirt, water and sun.</p>
<p>Here are the ingredients of my favorite recipe - a recipe for bread - that I have made almost every week during the past decade:</p>
<ul>
<li>7 1/4 cup flour</li>
<li>3 1/2 teaspoon salt</li>
<li>3 1/4 cup water</li>
<li>1 tablespoon yeast</li>
</ul>
<p>After thinking about this recipe almost a thousand times, and being inspired by a number of DIY cookbooks, <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> I started wondering - <em>why don&rsquo;t I make flour?</em> and <em>why don&rsquo;t I extract my own salt?</em> Flour and salt are ingredients that I usually buy from the store. However, what would happen if I included the recipe for salt and flour in the recipe for bread? What would the ingredients and instructions look like, and how long would this elongated recipe take to make?</p>
<p>Ingredients are usually restricted to the foods we can buy at the store. Lets remove this restriction and investigate the ingredients of ingredients. In order not to get too far I will define &ldquo;core ingredients&rdquo; that are irreducible (we don&rsquo;t need to specify the number of Carbon atoms in a loaf of bread). Core ingredients include soil, sun, water, and living organisms (cow, yeast, chickens).</p>
<h2 id="recipe-recursion">Recipe recursion</h2>
<p>In order to make bread from scratch,<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> we can take each ingredient in the original ingredient list and generate a list of ingredients for that. We can then use <a href="https://en.wikipedia.org/wiki/Recursion" target="_blank" >a mathematical concept known as &ldquo;recursion&rdquo;</a> - <em>the process a procedure goes through when one of the steps of the procedure involves invoking the procedure itself</em>.</p>
<p>In order to iterate through the recipe as a recursive process, we need a recursive definition of a recipe. For my purposes I created a list of axioms which define the recipe:</p>
<ol>
<li>Recipes are ingredients.</li>
<li>A recipe is composed of <em>reactants</em>, <em>products</em> and <em>instructions</em>. The <em>reactants</em> are a list of recipes needed to complete the reaction. The <em>products</em> are the recipes that result. The <em>instructions</em> explain the transformation (how much, how long, etc.)</li>
<li>The same <em>reactants</em> and the same <em>instructions</em> always create the same <em>products</em> (note: the inverse is not necessarily true).</li>
</ol>
<p>Using these axioms, I wrote a <a href="https://github.com/schollz/timetomakefood/blob/master/datav2/data.toml" target="_blank" >document containing lists of various recipes</a> which are known precursors to flour, salt, and many other foods.</p>
<h2 id="ingredients-to-really-make-bread-from-scratch">Ingredients to <em>really</em> make bread from scratch</h2>
<p><em>What is the recipe for flour?</em> It is basically grinding of wheat berries. <em>What is the recipe for wheat berries?</em> It is basically the winnowing of the grain from wheat. <em>What is the recipe for wheat berries?</em> &hellip;</p>
<p>These are the questions that I asked myself over and over again to do a recursive replacement of ingredients in the bread recipe, until the final core ingredients were reached. After this process I obtained the following new list of ingredients:</p>
<ul>
<li>Sun</li>
<li>Plot of soil</li>
<li>Water source</li>
<li>1 gallon seawater</li>
<li>3 1/4 cup water</li>
<li>1 tablespoon yeast</li>
</ul>
<p>&ldquo;Flour&rdquo; has been replaced by &ldquo;plot of soil, water source, and sun&rdquo; to grow the wheat and &ldquo;salt&rdquo; has been replaced by &ldquo;seawater.&rdquo; Of course, there is a process for converting these ingredients into the final loaf of bread, which can be visualized in the following:</p>
<p><img src="/img/bread-graph.png" alt="Visualization of the network for making bread. Ingredients are in green, intermediate foods are in yellow, and the final recipe is in red."></p>
<p>In the figure, it shows the ingredients (in red) and their intermediate products (in yellow) and the final recipe (in green). It turns out there is a good reason to buy flour at the store - there are a lot of steps and a lot of materials needed to create it! I bet it tastes good, though.</p>
<h2 id="time-to-make-bread-from-scratch">Time to make bread from scratch</h2>
<p>Of course, there is also a set of instructions for each step in this graph (40 instructions total!). Each instruction carries with it the amount of time to create the necessary intermediate product:</p>
<ul>
<li>Loaf of bread: 6 hours and 50 minutes</li>
<li>Salt: 2 weeks, 10 hours</li>
<li>Flour: Over 4 months</li>
</ul>
<p>In total, then, making this loaf of bread <em>from the very core ingredients</em> will take <strong>4 months, 3 weeks, 5 days, 14 hours, and 25 minutes</strong> (which includes the intermediate processes for grinding and threshing wheat).</p>
<h2 id="making-a-web-app">Making a web app</h2>
<p>I started compiling similar recipes for cookies, tortillas, noodles and found it very interesting to peruse their recipes to the core ingredients.</p>
<p>The tool is open-source, and you can edit the code and add your own recipes: <a href="https://github.com/schollz/recursive-recipes" target="_blank" >github.com/schollz/recursive-recipes</a>. You can also try it online at <a href="https://recursiverecipes.schollz.com" target="_blank" >recursive.recipes</a>.</p>
<p>I am glad grocery stores exist, because I like making bread every week, and not three times a year.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Alana Chernila (2012). <em>The Homemade Pantry: 101 Foods You Can Stop Buying and Start Making.</em> Clarkson Potter/Publishers. ISBN 978-0-307-88726-9.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Jennifer Reese (16 October 2012). <em>Make the Bread, Buy the Butter: What You Should and Shouldn&rsquo;t Cook from Scratch&ndash;Over 120 Recipes for the Best Homemade Foods.</em> Simon and Schuster. ISBN 978-1-4516-0588-4.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Miyoko Schinner (16 June 2015). <em>The Homemade Vegan Pantry: The Art of Making Your Own Staples.</em> Potter/TenSpeed/Harmony. ISBN 978-1-60774-678-2.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><em>Skip this section if you are not a nerd.</em>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Watercoloring with neural networks</title>
	    <image>https://schollz.com/img/heron3.jpg</image>
      <link>/tinker/watercolor/</link>
      <pubDate>Wed, 14 Jun 2017 05:48:49 -0700</pubDate>
      
      <guid>/tinker/watercolor/</guid>
      <description><![CDATA[<p>Can a neural network help me improve my art? Could I take a photo, paint it, and then use a neural network to render the original photo in the style of my painting to make it better? Could I learn something from it?</p>
<h2 id="some-background">Some background</h2>
<p>I started thinking about these questions because I’ve been reading about neural networks, and I’ve seen a lot of these neural networks create new works of art by rendering a photo in the style of a famous painting<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<div style="text-align:center;">
<img src="/img/rosseauscat.jpg" alt='The "content" of Jean-Jacques Rosseau’s painting is recognizable as a cat, despite the painting "style"' style="max-width:200px;"/>
</div>
<p>Neural networks are complicated and worth reading about<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. Basically, a neural network is a system composed of computational “neurons” linked up in a way that is similar to the brain that try to solve problems. The connections between these neurons are given specific weights, as they learn how to classify one thing from another. A neural network understands a painting by looking at its specific features.</p>
<p>Researchers have discovered that paintings have two main transferable features: <em>&ldquo;content&rdquo;</em> and <em>&ldquo;style&rdquo;</em>. When we look at a painting, like Jean-Jacques Rosseau’s cat, we experience both the <em>content</em> and the <em>style</em> of the painting simultaneously, but we can (usually) identify them separately. The <em>content</em> is the the subject of that the painter is trying to convey (the cat), where <em>style</em> is the manner which they convey it (bold colors and strokes, awkward proportions in the legs). Rosseau cannot fool us into thinking he did not paint a cat, even though he did an awfully strange representation of it.</p>
<p><img src="/img/tubingen_starry.png" alt="Neural network rendered painting in the style of Starry Night with the content from Neckarfront houses. Photo credit: github.com slash jcjohnson"></p>
<p>Recently, a paper by Gatys, Ecker, and Bethge<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> was able to show that <em>representations of content and style are separable</em>. They used an algorithm that makes use of a convolutional neural network<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> and demonstrated the separability of content and style by transferring the style of a <em>Starry Night</em> by Van Gogh (i.e. the iconic swirls of dark blue pastel) to a photograph of the Neckarfront houses of Tubingen, Germany.</p>
<p>The genius of the paper by Gatys, Ecker, and Bethge<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> is that they leverage neural networks for classifying local features in images to extract the representations of content. The neural network used here is the VGG-Network by Simonyan and Zisserman which is the state-of-the-art for identifying things in images<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.</p>
<p>To transfer <em>Starry Night</em> to Neckarfront, Gatys, Ecker, and Bethge simply exploited the neural network to copy the set of neurons that finds representations of content (which parts of the image are the “building”), and then using another layer that extracts the style, or texture (i.e. non-features that are local in context, like the swirls in Van Gogh’s painting).</p>
<h2 id="making-a-neural-network-to-paint-with-my-style">Making a neural network to paint with my style</h2>
<p>To make a neural network paint like me, I will first select a photo and then paint the photo myself. Then I will use my painting to generate a neural network rendering from the original photo. That is, I will use the <em>style</em> from the painting I create and transfer it to the <em>content</em> of the photo that I used to create the painting. Technically speaking, I used the algorithm from Gatys, Ecker, and Bethge, as coded by <a href="https://github.com/awentzonline/image-analogies" target="_blank" >jcjohnson</a> compiled with the CUDA backend to use on a GTX 1080Ti.</p>
<p><img src="/img/heron1.jpg" alt="Source photo for painting"></p>
<p>I started with a photo of a heron.</p>
<p><img src="/img/heron2.jpg" alt="My painting"></p>
<p>Then, I create my own Gouache painting of the photo.</p>
<p><img src="/img/heron3.jpg" alt="Transfer of my style to original photo"></p>
<p>Finally, I used Torch<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> to transfer the <em>style</em> of my painting to the <em>content</em> of the photo to create a neural network rendering.
The results are impressive. The neural network uses my color palette and some of my strokes, but seems to suppress a lot of my mistakes and more precisely articulates the original photo. For example - the wings that I painted are very unordered and sloppy, but the neural network used the original wings and used the idea of my painting to fix them.</p>
<h2 id="the-neural-network-becomes-the-teacher">The neural network becomes the teacher</h2>
<p>The heron is not an outlier. Almost every painting I create can be re-imagined by a neural network to be rendered as a much cleaner and more professional painting<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>. The machine makes a professional out of a novice.</p>
<p>Here are more examples showing the original photo (left), my painting of the photo (middle) and the neural network rendering of the photo using the style of my painting (right). After looking at them quite a bit, I can see some simple things I can do to improve my own painting.</p>
<p>Click on any of the paintings to see them in full resolution.</p>
<h3 id="cat">Cat</h3>
<p>I like how the neural network put more white around the eyes and also patches of gray to shadow the eye and mouth. I could have also been more liberal with my striping on the arm.</p>
<p><a href="/img/cat.jpg" ><img src="/img/cat.jpg" alt=""></a></p>
<h3 id="bison">Bison</h3>
<p>Though I like the original watercoloring better, I think the neural network does a better job on the bison&rsquo;s hair on its head - using a reddish color and blending it out.</p>
<p><a href="/img/bison_wc.jpg" ><img src="/img/bison_wc.jpg" alt=""></a></p>
<h3 id="koala">Koala</h3>
<p>My mistakes fixed by the neural network - I made the yellow around the baby koala is too wide, and its head too light. I love the neural network ears - they aren&rsquo;t outlined so it gives them a lighter touch, and the fur on the big koala has more brush strokes which makes it more dynamic.</p>
<p><a href="/img/koala.jpg" ><img src="/img/koala.jpg" alt=""></a></p>
<h3 id="deer-and-cat">Deer and cat</h3>
<p>I also think my rendition for the watercolor is very good, although I wish I had gotten the proportions better, as it looks a lot nicer. Also a thinner outline would have been better, as the neural network does. I also love how the neural network reinterprets the background - putting in more orange and gray which complements the deer and cat.</p>
<p><a href="/img/deer.jpg" ><img src="/img/deer.jpg" alt=""></a></p>
<h3 id="fox">Fox</h3>
<p>I had a lot of trouble on the tail of this fox, and the neural network has a great way of doing it - simply dab the outside and then blend in the grey and red. I also love the purple on the fox hind leg, which I was not bold enough in bringing out on my own painting.</p>
<p><a href="/img/fox.jpg" ><img src="/img/fox.jpg" alt=""></a></p>
<h3 id="dog-and-owl">Dog and owl</h3>
<p>I love my painting here as well, but the proportions are wrong, my dog is far to narrow. I also could have shadowed the snout a little differently, to make the sides of the head stand out more, like in the neural network.</p>
<p><a href="/img/dogandowl.jpg" ><img src="/img/dogandowl.jpg" alt=""></a></p>
<h3 id="mountain-goat">Mountain Goat</h3>
<p>A white animal is <em>very</em> difficult for me, but I think the neural network shows a possible solution: focus on the middle with gray/blue and then on the outside with yellow.</p>
<p><a href="/img/mtngoat.jpg" ><img src="/img/mtngoat.jpg" alt=""></a></p>
<h2 id="conclusion">Conclusion</h2>
<p>It seems a computer is better at painting than I am. But seriously, this is a rather neat illustration of the separation of <em>content</em> and <em>style</em>. In the future I would like to instead train my own neural network with a corpus of my photos and illustrations to transfer my style to photos. In this case, I imagine it will be agnostic to <em>content</em> but will merely encode my style. I have to learn how to train neural networks first, though.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Some great resources on Github are <a href="https://github.com/awentzonline/image-analogies" target="_blank" >awentzonline&rsquo;s image analogies</a> and <a href="https://github.com/jcjohnson/neural-style" target="_blank" >jcjohnsons&rsquo;s Torch implementation of neural styles</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://neuralnetworksanddeeplearning.com/" target="_blank" >Here is a great, free, online book</a> written by Michael Nielsen which gives a great introduction to neural networks.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Gatys, Leon A., Alexander S. Ecker, and Matthias Bethge. &ldquo;A neural algorithm of artistic style.&rdquo; arXiv preprint arXiv:1508.06576 (2015).&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>A convolutional neural network is a special kind of neural network that links up the computer neurons in a way that mimics how the visual cortex operates.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Simonyan, Karen, and Andrew Zisserman. &ldquo;Very deep convolutional networks for large-scale image recognition.&rdquo; arXiv preprint arXiv:1409.1556 (2014).&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>The cases where the neural network did poorly was in paintings where I left the background completely white.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>Autological words and quines</title>
	    <image>https://schollz.com/img/auto.svg</image>
      <link>/tinker/quines/</link>
      <pubDate>Sun, 04 Jun 2017 09:48:59 -0600</pubDate>
      
      <guid>/tinker/quines/</guid>
      <description><![CDATA[<p>The word <em>&ldquo;noun&rdquo;</em> is a singular word. It is the only word that is what it is. Such a word is an <em>&ldquo;autological word&rdquo;</em> as they posses the property that they express. To determine if <em>X</em> is autological, it requires answering yes to any of the following questions:</p>
<ul>
<li>Is <em>X</em> a <em>X</em> word?</li>
<li>Is &ldquo;<em>X</em>&rdquo;, <em>X</em>?</li>
</ul>
<p>Some great examples of autological words are &ldquo;polysyllabic&rdquo;, &ldquo;unhyphenated&rdquo;, &ldquo;harmless&rdquo;, &ldquo;pentasyllabic&rdquo;, &ldquo;real&rdquo;, and &ldquo;unique&rdquo;. Dr. Henry Segerman has <a href="http://www.segerman.org/autological.html" target="_blank" >a long list of these autological words on his website</a>.</p>
<p>There is analog to autological words in the digital world: <em>&ldquo;quines&rdquo;</em>. <a href="https://en.wikipedia.org/wiki/Quine_%28computing%29" target="_blank" >Quines</a> are non-empty programs that produce a copy of its own source-code as its only output. A Python quine is rather simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1">1</a></span><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="s1">&#39;s = </span><span class="si">%r</span><span class="se">\n</span><span class="s1">print(s</span><span class="si">%%</span><span class="s1">s)&#39;</span>
</span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2">2</a></span><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">s</span><span class="o">%</span><span class="n">s</span><span class="p">)</span>
</span></span></code></pre></div><p>A <a href="https://play.golang.org/p/pVBds0oHrO" target="_blank" >Golang quine</a> is also pretty simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl"><span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl">    <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;%s%c%s%c\n&#34;</span><span class="p">,</span> <span class="nx">s</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="nx">s</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl"><span class="kd">var</span> <span class="nx">s</span> <span class="p">=</span> <span class="s">`package main
</span></span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl"><span class="s">import &#34;fmt&#34;
</span></span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl"><span class="s">func main() {
</span></span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl"><span class="s">    fmt.Printf(&#34;%s%c%s%c\n&#34;, s, 0x60, s, 0x60)
</span></span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl"><span class="s">}
</span></span></span><span class="line"><span class="ln" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a></span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a></span><span class="cl"><span class="s">var s = `</span>
</span></span></code></pre></div><p>Here&rsquo;s an <strong>Idea</strong>: what if you wrote a program that quinified your program? It can take any generic program and then re-write so that it also produces a code of itself.</p>
<p>And here&rsquo;s a <strong>Thought</strong>: what are other things in the world that are quines or autologically predisposed? Are there other things that are in themselves representations of itself? Without resorting to <em><a href="https://en.wikipedia.org/wiki/The_Society_of_the_Spectacle" target="_blank" >The Society of the Spectacle</a></em>, of course.</p>
]]></description>
    </item>
    
    <item>
	    <title>Better book suggestions</title>
	    <image>https://schollz.com/img/lakjsd.PNG</image>
      <link>/tinker/books/</link>
      <pubDate>Tue, 02 May 2017 09:41:01 -0600</pubDate>
      
      <guid>/tinker/books/</guid>
      <description><![CDATA[<p>I read about 30–40 books a year. Not to be morbid, but this equates to only reading about 3,000 books in my lifetime. Since there are millions of books that exist,<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> it means that I can only consume a fraction of a fraction of a percent of them. This exceptionally limited scope of my eventual reading repertoire is my motivation to find a systematic method to acquiring <em>good</em> book suggestions.</p>
<h2 id="current-recommendations-are-bad">Current recommendations are bad</h2>
<p>I’ve noticed that there are five major problems that prevent me from continuing to use the major services like Goodreads and Amazon for book recommendations.</p>
<h3 id="problem-1-suggested-books-are-the-wrong-genre">Problem 1: Suggested books are the wrong genre</h3>
<p>One of the best approaches to finding a new book to read is to look for books with a similar genre. The genre of <em>Weaveworld</em> is classified as “dark fantasy horror” according to Amazon, which is a good approximation of the book. And since this is a pretty specific genre it should provide good recommendations that are similar, right? Unfortunately, no.</p>
<p>On Amazon I can lookup the top sellers for this genre, “dark fantasy horror,” and try to find a book from those. Here is what I see, looking at the top six sellers on this particular day from Amazon:</p>
<p><img src="/img/books2.png" alt="Amazon’s Best Sellers for “dark fantasy horror”"></p>
<p>If the titles and the book covers don’t give it away (although you should not judge a book by its cover), three of the six top sellers for the “dark fantasy horror” genre have descriptions that contain phrases like “sexy”, “adult urban” or “a budding romance.” I do not judge people that like these books, but I would argue that these are in the wrong category. Even though these might have elements of “dark fantasy horror”, it seems that their main themes are adult. (You are probably thinking, this can be solved using multiple genres. You are right, read on!).</p>
<p>Basically the top sellers from Amazon for the genre “dark fantasy horror” contains little, if any, actual “dark fantasy horror” and its a waste of time to go browsing through it.</p>
<h3 id="problem-2-suggested-books-are-sequels">Problem 2: Suggested books are sequels</h3>
<p>Instead of looking at top sellers, one can also simply search the genre. This will run into another problem. Here are three sequential results on the first page of searching for “dark fantasy horror” from Amazon:</p>
<p><img src="/img/books3.png" alt="Amazon’s search results for “dark fantasy horror”"></p>
<p>Of course, one of these has “steamy sex” as a description (Problem #1), but even more problematic is that <em>all three of these books are sequels</em>. I would never recommend you to read a sequel before reading the first book, with exception of a few books — like <a href="https://en.wikipedia.org/wiki/The_Book_of_Merlyn" target="_blank" >The Book of Merlyn</a> is a great one to read no matter what. Currently, though, you have to trudge through a bunch of sequels because there is no way to filter them out on Amazon or Goodreads.</p>
<h3 id="problem-3-book-suggestions-come-from-only-one-or-a-few-authors">Problem 3: Book suggestions come from only one (or a few) authors</h3>
<p>If we do a similar search on Goodreads, we will find another problem. Here are the top results for searching the genre “dark horror fantasy”:</p>
<p><img src="/img/books4.png" alt="Goodread’s results for “dark horror fantasy”"></p>
<p>These search results are also problematic in terms of providing good recommendations as <em>four of these six books have the same author!</em> The number of distinct authors increases very slowly as you go through the search results from Goodreads. This is not helpful because I believe you should be recommending a <em>new author</em> just as much as a new book. I don’t need a recommendation service to just go look up books from the same author.</p>
<h3 id="problem-4-internet-people-rarely-share-similar-tastes">Problem 4: Internet people rarely share similar tastes</h3>
<p>I enjoy when friends give me recommendations since I have a good idea of whether (or not) we share taste in books and I can easily decided to trust them (or not). I don’t enjoy having recommendations from random people based on their web history, since you never know if someone just read some book and secretly didn’t like it but told the Internet they did.</p>
<p>For some special people these types of recommendations might work (e.g. if you are just starting out on a genre). But usually these type of results are derivative. Amazon will show me books from “Customers who bought this item” and Goodreads will show me “members who liked X also liked” but the results are similar. For example, here are Amazon’s:</p>
<p><img src="/img/books5.png" alt="Amazons user-based recommendations (sorry again this looks like an ad"></p>
<p>The first two books are possibly good recommendations, until you see that both those books are two of the most popular books in the genre <em>of all time</em>. So if you are new to the genre they are good, but I am not, so of course I have read those before. The other books are both books from the author of the <em>Weaveworld</em>, so I do not want those suggestions (Problem #3).</p>
<p>I want a <em>new</em> recommendation from a <em>different</em> author.</p>
<h3 id="problem-5-recommendations-are-hard-to-parse">Problem 5: Recommendations are hard to parse</h3>
<p>You can certainly find a good book suggestion from Goodreads or Amazon, but it might take a long time. It seems that these sites are designed to keep you looking, in fact, because they have an incentive to elude you from getting complete information quickly. Because of this, I have given up with Goodreads and Amazon. There are other services, like whatshouldireadnext.com, but this hardly provides similar recommendations for <em>Weaveworld by Clive Barker</em>, as <a href="http://www.whatshouldireadnext.com/isbn/0006483003" target="_blank" >it suggested a book about LA ghost stories and a book about Cambodia war crimes</a>.</p>
<p>In light of this, I propose a simple solution.</p>
<h2 id="my-ideal-book-recommendation-service">My ideal book recommendation service</h2>
<p>To make a book recommendation service that actually works, I would avoid Problems #1–3 using simple filters. And it will avoid Problem #5 by being quick and simple — no sign-ups, no ads, no hard-to-navigate user interface. It would avoid Problem #4 by using probabilistic genres.</p>
<h3 id="what-are-probabilistic-genres">What are probabilistic genres?</h3>
<p>What I call a probabilistic genre could be called many things: the book’s genome, the hierarchical classification, etc. The basic idea is that each book has multiple genres with different weights or emphasis.</p>
<p>The probabilistic genres can be calculated using the Goodreads API. For example, <em>Weaveworld</em> <a href="https://www.goodreads.com/book/show/52640.xml?key=zmAD3os2k0K8o96QPI7Fw" target="_blank" >has the following set of genres according to Goodreads API</a>:</p>
<pre><code>&lt;shelf name=”to-read” count=”6532&quot;/&gt;
&lt;shelf name=”fantasy” count=”1017&quot;/&gt;
&lt;shelf name=”currently-reading” count=”711&quot;/&gt;
&lt;shelf name=”horror” count=”684&quot;/&gt;
&lt;shelf name=”fiction” count=”280&quot;/&gt;
&lt;shelf name=”favorites” count=”255&quot;/&gt;
&lt;shelf name=”owned” count=”104&quot;/&gt;
&lt;shelf name=”clive-barker” count=”97&quot;/&gt;
&lt;shelf name=”dark-fantasy” count=”62&quot;/&gt;
&lt;shelf name=”default” count=”61&quot;/&gt;
&lt;shelf name=”books-i-own” count=”60&quot;/&gt;
&lt;shelf name=”sci-fi-fantasy” count=”46&quot;/&gt;
&lt;shelf name=”science-fiction” count=”34&quot;/&gt;
&lt;shelf name=”sci-fi” count=”31&quot;/&gt;
</code></pre>
<p>While <em>Weaveworld</em> is defined by Amazon and Goodreads to be “dark fantasy horror”, according to these shelves (and ignoring non-genres) it is actually mostly fantasy (1,017 counts), horror (684 counts), with a little science fiction (~100 counts). It might be better classified as “fantasy horror fiction sci-fi.”</p>
<p>Just for comparison, to see the benefit of these probabilistic genres, recall the very first recommendation from Amazon in the genre “dark fantasy horror”: <em>Agent of Enchantment by C.N. Crawford</em>? Here are <a href="https://www.goodreads.com/book/show/34776155.xml?key=zmAD3os2k0K8o96QPI7Fw" target="_blank" >its top genres from the Goodreads API</a>:</p>
<pre><code>&lt;shelf name=”urban-fantasy” count=”8&quot;/&gt;
&lt;shelf name=”fantasy” count=”6&quot;/&gt;
&lt;shelf name=”paranormal” count=”4&quot;/&gt;
&lt;shelf name=”magic” count=”3&quot;/&gt;
&lt;shelf name=”fae” count=”3&quot;/&gt;
&lt;shelf name=”favorites” count=”3&quot;/&gt;
&lt;shelf name=”maybe” count=”3&quot;/&gt;
&lt;shelf name=”netgalley” count=”3&quot;/&gt;
&lt;shelf name=”mystery” count=”2&quot;/&gt;
&lt;shelf name=”fairies” count=”2&quot;/&gt;
&lt;shelf name=”adult” count=”2&quot;/&gt;
&lt;shelf name=”romance” count=”2&quot;/&gt;
</code></pre>
<p>Clearly, <em>Agent of Enchantment</em> would be better classified as “urban fantasy paranormal mystery adult romance” and it is definitely not similar to “fantasy horror fiction sci-fi” of <em>Weaveworld</em>. In fact, its not even close to the genre of point “dark horror fantasy”, because “horror” doesn’t even show up.</p>
<p>Anyways, I digress. These genres can be determined in a more quantified way by calculating the percentage of each genre (ignoring non-genres) so <em>Weaveworld</em> might be approximated as “45% fantasy, 25% horror, 15% fiction, 6% dark fantasy, 5% science fiction and 4% sci-fi fantasy”, based solely on the counts. To find a recommendation then, I just need to go through books with similar genres and calculate a metric that determines the “distance” between the two books. Once I have a list of distances, I can sort them and find the closest distances and perform the filtering I want (no similar authors, no sequels).</p>
<h2 id="coding-my-solution1">Coding my solution<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></h2>
<p>Now that I had my basic plan of attack, I decided to implement the ideas as a simple single-page app (SPA). I found a cheap domain name: booksuggestions.ninja and made a cute logo and set to work.</p>
<h3 id="step-1-collecting-data">Step 1: Collecting data</h3>
<p>I first collected the data for books from Goodreads API and the Google Books API. This took several weeks.</p>
<p>Realistic ratings from Google Books were determined using a <a href="https://en.wikipedia.org/wiki/Bayesian_average" target="_blank" >Bayesian average</a> which accounted for the average rating count and value <em>in that genre.</em> I especially used genre-specific ratings because I have seen on Amazon how all the “romantic” novels have absurdly high recommendations which means that certain books may self-select groups of people who are predisposed to rating things a common way.</p>
<h3 id="step-2-processing-similarities">Step 2: Processing similarities</h3>
<p>I wrote a program in <a href="https://golang.org/" target="_blank" >Go</a> to process the similarities. This program is pretty simple — it loads all the books into a giant map, and then for each book it traverses the map and calculates the distances from it to every other book. The distances are computed as the total sum of the absolute difference between two of the same genres. Once the distances are computed, it sorts the list and outputs a file containing its matches. Simplicity does not equate speed in this case, as the matching takes about 24 days to calculate. Luckily I can use <a href="https://gobyexample.com/worker-pools" target="_blank" >Go worker pools</a>, so it only takes 3 days, during which my looks like this:</p>
<p>I wish I had checked for bugs before running this, because after running it I found I had to start over and wait <em>another 3 days</em> to calculate the similarities a second time.</p>
<h3 id="step-3-building-a-database">Step 3: Building a database</h3>
<p>After determine the book-to-book similarity, I wrote another Go program to generate a SQL file for a database. The SQL file is used to construct the database with the command (the is a cool trick to see how long it takes to push the data into the DB). Of course, this could have been done in Go as well but I’m lazy.</p>
<p>Speaking of laziness, I then wrote a Python script to generate a bash script that would generate text files that contain entries from common searches (e.g. searches for the best books from a specific genre). Gotta love that meta-programming!</p>
<h3 id="step-4-building-a-website">Step 4: Building a website</h3>
<p>I built a backend in Go using <a href="https://github.com/gin-gonic/gin" target="_blank" >the Gin framework</a>. The back-end basically performs SQL queries in the database. The front-end is a webpage with a text box to enter search for books or select a genre. The communication with the backend is written in jQuery. I realized I could have used this opportunity to learn React + Redux, but I just wanted to do the front-end fast rather than well. Even so, I learned some neat things and ended up building my own little <a href="https://developer.mozilla.org/en-US/docs/Web/API/History_API" target="_blank" >DOM history manipulation</a> so you can use the back arrow in the app - it’s a small detail but it makes it feel much better.</p>
<h3 id="step-5-handling-user-input">Step 5: Handling user input</h3>
<p>I quickly realized that I would need an approximate string matching library to help me find the book that a user is searching. This is a particulary daunting task since there are over 2.5 million books and Levenshtein would be far to slow (it can takes tens of seconds!). I ended up writing my own little algorithm that is based off a bag-of-words approach, and given the right parameters it can find matches in less than 30 milliseconds! I actually made this part of the program a microservice because it eats up a lot of RAM so I do not want to run multiple instances of it if I choose to. The microservice and the Go library are available <a href="https://github.com/schollz/closestmatch" target="_blank" >at github.com/schollz/closestmatch</a>.</p>
<h3 id="step-6-try-it-out">Step 6: Try it out!</h3>
<p>Now that I had the technical details implemented, I went to my new website and tried to find a suggestion!</p>
<h2 id="result-what-book-should-i-read-after-weaveworld">Result: What book should I read after Weaveworld?</h2>
<p>To get a baseline I asked <em>real people</em> what I should read next and they suggested: <em>Imagica by Clive Barker</em>, stuff by Neil Gaiman books,* The Talisman by Stephen King. *I’ve already read all of Neil Gaiman books, and have somehow missed out on reading any Stephen King. Still, these were pretty good recommendations from people familiar with the book I’m reading.</p>
<p>After getting real recommendations, I turned to the <em>Book Suggestions Ninja</em> and entered <em>Weaveworld by Clive Barker.</em> The <a href="https://www.booksuggestions.ninja/book/weaveworld-by-clive-barker/" target="_blank" >Book Suggestions Ninja suggested a bunch of books that are similar to Weaveworld</a>.</p>
<p>The top recommendation is <em>The Library at Mount Char by Scott Hawkins</em>. This sounds like a great recommendation - a &ldquo;horrifying and hilarious&rdquo; book about gods and secrets with weird and esoteric characters. The other recommendations are great too - <em>Imajica</em> and <em>Night Watch</em> are both books I&rsquo;ve been interested. <em>The Stand</em> is also a frequent recommendation. Further down there are great books I&rsquo;ve read before, like <em>The Lies of Locke Lamora</em> and <em>The Warded Man</em>, but for the most part all of these books are really good <strong>new</strong> recommendations. Looks like I will have plenty of reading to do!</p>
<p><img src="/img/lakjsd.PNG" alt="Example of Book Suggestions Ninja"></p>
<h3 id="choosing-my-next-book-to-read">Choosing my next book to read</h3>
<p>I chose two books to read - the top recommendation and the 19th suggestion which are both listed as <em>SO good</em>. The top recommendation, <em>The Library at Mount Char by Scott Hawkins</em> was really <em>SO good</em> in that I couldn&rsquo;t put it down and finished it in four days. The other book is <em>Hard Magic by Larry Correia</em> which has these elements of magical realism and horror and fantasy that I liked about <em>Weaveworld</em>. This is impressive that the suggested books stay so close to the original, even this far down the list!</p>
<p>I like these recommendations, a lot. They get straight to the point and have some books that I know are good, which makes me think the rest could be good too. These recommendations literally took me 6 seconds of searching and 2 minutes of scanning the web page.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The result of this rather long article and four weeks of work and 1,600 lines of code is <a href="https://www.booksuggestions.ninja" target="_blank" >www.booksuggestions.ninja</a>.</p>
<p>No recommendation will probably ever be better than your friends. However, if you carefully analyze genres, look at a diverse list of authors, and filter out sequels, you can find great recommendations. This is what my app, <a href="https://www.booksuggestions.ninja/" target="_blank" >Book Suggestions Ninja</a>, tries to do — maybe it will work for you too? If it does, please let me know, tweet me <a href="https://twitter.com/nicenovelninja" target="_blank" >@nicenovelninja</a> (tweet me if it doesn’t work, too).</p>
<p>In any case, <em>I</em> found a new book to read after finishing <em>Weaveworld</em> which is all I could really ask.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Probably a low estimate&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>For nerds only&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
    </item>
    
    <item>
	    <title>How to use Wordpress with Docker</title>
	    <image>https://schollz.com/img/wordpress.png</image>
      <link>/tinker/wordpress/</link>
      <pubDate>Wed, 11 Jan 2017 13:54:51 -0600</pubDate>
      
      <guid>/tinker/wordpress/</guid>
      <description><![CDATA[<p>I like wordpress, but it is very intensive to get working, as it uses a lot of PHP and requires SQL. Of course, nowadays you can do everything in Docker, so here is my method for getting wordpress to work great on Docker.</p>
<p>Using Docker, I was able to get 2 blogs run on the smallest DigitalOcean droplet (which was already running two dozen other things). Each blog required 360MB of RAM, and the total Docker space was 3.1G.</p>
<p>The following instructions will enable you to go from zero to Wordpress in about six minutes.</p>
<h3 id="setup">Setup</h3>
<p>First make a file <code>docker-compose.yml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln" id="hl-0-1"><a class="lnlinks" href="#hl-0-1"> 1</a></span><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;2&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-2"><a class="lnlinks" href="#hl-0-2"> 2</a></span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-3"><a class="lnlinks" href="#hl-0-3"> 3</a></span><span class="cl"><span class="w"></span><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-4"><a class="lnlinks" href="#hl-0-4"> 4</a></span><span class="cl"><span class="w">   </span><span class="nt">db</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-5"><a class="lnlinks" href="#hl-0-5"> 5</a></span><span class="cl"><span class="w">     </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mysql:5.7</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-6"><a class="lnlinks" href="#hl-0-6"> 6</a></span><span class="cl"><span class="w">     </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-7"><a class="lnlinks" href="#hl-0-7"> 7</a></span><span class="cl"><span class="w">       </span>- <span class="l">db_data:/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-8"><a class="lnlinks" href="#hl-0-8"> 8</a></span><span class="cl"><span class="w">     </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-9"><a class="lnlinks" href="#hl-0-9"> 9</a></span><span class="cl"><span class="w">     </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-10"><a class="lnlinks" href="#hl-0-10">10</a></span><span class="cl"><span class="w">       </span><span class="nt">MYSQL_ROOT_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-11"><a class="lnlinks" href="#hl-0-11">11</a></span><span class="cl"><span class="w">       </span><span class="nt">MYSQL_DATABASE</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-12"><a class="lnlinks" href="#hl-0-12">12</a></span><span class="cl"><span class="w">       </span><span class="nt">MYSQL_USER</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-13"><a class="lnlinks" href="#hl-0-13">13</a></span><span class="cl"><span class="w">       </span><span class="nt">MYSQL_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-14"><a class="lnlinks" href="#hl-0-14">14</a></span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-15"><a class="lnlinks" href="#hl-0-15">15</a></span><span class="cl"><span class="w">   </span><span class="nt">wordpress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-16"><a class="lnlinks" href="#hl-0-16">16</a></span><span class="cl"><span class="w">     </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-17"><a class="lnlinks" href="#hl-0-17">17</a></span><span class="cl"><span class="w">       </span>- <span class="l">db</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-18"><a class="lnlinks" href="#hl-0-18">18</a></span><span class="cl"><span class="w">     </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress:latest</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-19"><a class="lnlinks" href="#hl-0-19">19</a></span><span class="cl"><span class="w">     </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-20"><a class="lnlinks" href="#hl-0-20">20</a></span><span class="cl"><span class="w">       </span>- <span class="s2">&#34;8001:80&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-21"><a class="lnlinks" href="#hl-0-21">21</a></span><span class="cl"><span class="w">     </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-22"><a class="lnlinks" href="#hl-0-22">22</a></span><span class="cl"><span class="w">     </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-23"><a class="lnlinks" href="#hl-0-23">23</a></span><span class="cl"><span class="w">       </span><span class="nt">WORDPRESS_DB_HOST</span><span class="p">:</span><span class="w"> </span><span class="l">db:3306</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-24"><a class="lnlinks" href="#hl-0-24">24</a></span><span class="cl"><span class="w">       </span><span class="nt">WORDPRESS_DB_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-25"><a class="lnlinks" href="#hl-0-25">25</a></span><span class="cl"><span class="w">     </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-26"><a class="lnlinks" href="#hl-0-26">26</a></span><span class="cl"><span class="w">       </span>- <span class="l">/path/to/some/folder/on/your/computer/wp_html:/var/www/html</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-27"><a class="lnlinks" href="#hl-0-27">27</a></span><span class="cl"><span class="w"></span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln" id="hl-0-28"><a class="lnlinks" href="#hl-0-28">28</a></span><span class="cl"><span class="w">    </span><span class="nt">db_data</span><span class="p">:</span><span class="w">
</span></span></span></code></pre></div><p>Then, to start just use (add <code>-d</code> for daemon mode)</p>
<pre tabindex="0"><code>docker-compose up
</code></pre><p>If you need to stop it just use</p>
<pre tabindex="0"><code>docker-compose stop
</code></pre><h3 id="reverse-proxy">Reverse proxy</h3>
<p>If you are using a domain name, you can easily use Caddy as a reverse proxy. Here is an example <code>Caddyfile</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1">1</a></span><span class="cl">http://blogname <span class="o">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2">2</a></span><span class="cl">    proxy / 127.0.0.1:8006 <span class="o">{</span>
</span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3">3</a></span><span class="cl">        transparent
</span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4">4</a></span><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5">5</a></span><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Make sure to goto your blog and update it accordingly to http://blogname.</p>
<p>For using SSL, <a href="https://www.heavymetalcoder.com/how-to-get-wordpress-working-with-https-behind-a-reverse-proxy/" target="_blank" >checkout this blog</a> which describes the process for correctly configuring Wordpress to allow it.</p>
<h3 id="backuprestore">Backup/Restore</h3>
<p>Wordpress on docker is also nice because its very easy to move. I basically <a href="https://libertyseeds.ca/2015/11/24/Backup-migration-and-recovery-with-WordPress-and-Docker-Compose/" target="_blank" >copied the instructions here</a> to get a simple way to backup and restore a Wordpress instance.</p>
<p>To backup:</p>
<pre tabindex="0"><code>docker exec -i wordpress_db_1 mysqldump --user=wordpress --password=wordpress wordpress &gt; backup.sql
tar -czvf wp_html.tar.gz wp_html
</code></pre><p>To restore:</p>
<pre tabindex="0"><code>docker exec -i wordpress_db_1 mysql --user=wordpress --password=wordpress wordpress &lt; backup.sql
tar -xvzf wp_html.tar.gz 
</code></pre>]]></description>
    </item>
    
    <item>
	    <title>cowyo.com</title>
	    <image>https://schollz.com/img/projects/cowyo.jpg</image>
      <link>/tinker/cowyo/</link>
      <pubDate>Mon, 15 Feb 2016 07:50:07 -0700</pubDate>
      
      <guid>/tinker/cowyo/</guid>
      <description><![CDATA[<p><em>cowyo</em> is a self-contained wiki server that makes jotting notes easy and <em>fast</em>. The most important feature here is <em>simplicity</em>. Other features include versioning, page locking, self-destructing messages, encryption, and listifying. You can <a href="https://github.com/schollz/cowyo/releases/latest" target="_blank" >download <em>cowyo</em> as a single executable</a> or install it with Go. Try it out at <a href="https://cowyo.com" target="_blank" >https://cowyo.com</a>.</p>
<p>There is now <a href="https://github.com/schollz/cowyodel" target="_blank" >a command-line tool, <em>cowyodel</em></a> to interact with <em>cowyo</em> and transfer information between computers with only a code phrase: <a href="https://github.com/schollz/cowyodel" target="_blank" >schollz/cowyodel</a>.</p>
<p><em>cowyo</em> is straightforward to use. Here are some of the basic features:</p>
<h3 id="publishing">Publishing</h3>
<p>If you hover the the top left button (the name of the page) you will see the option &ldquo;Publish&rdquo;. Publishing will add the page to the <code>sitemap.xml</code> for crawlers to find. It will also default that page to go to the <code>/read</code> route so it can be easily viewed as a single page.</p>
<h3 id="view-all-the-pages">View all the pages</h3>
<p>To view the current list of all the pages goto to <code>/ls</code>.</p>
<h3 id="editing">Editing</h3>
<p>When you open a document you&rsquo;ll be directed to an alliterative animal (which is supposed to be easy to remember). You can write in Markdown. Saving is performed as soon as you stop writing. You can easily link pages using [[PageName]] as you edit.</p>
<p><img src="http://i.imgur.com/vEs2U8z.gif" alt="Editing"></p>
<h3 id="history">History</h3>
<p>You can easily see previous versions of your documents.</p>
<p><img src="http://i.imgur.com/CxhRkyo.gif" alt="History"></p>
<h3 id="lists">Lists</h3>
<p>You can easily make lists and check them off.</p>
<p><img src="http://i.imgur.com/7xbauy8.gif" alt="Lists"></p>
<h3 id="locking">Locking</h3>
<p>Locking prevents other users from editing your pages without a passphrase.</p>
<p><img src="http://i.imgur.com/xwUFV8b.gif" alt="Locking"></p>
<h3 id="encryption">Encryption</h3>
<p>Encryption is performed using AES-256.</p>
<p><img src="http://i.imgur.com/rWoqoLB.gif" alt="Encryption"></p>
<h3 id="self-destructing-pages">Self-destructing pages</h3>
<p>Just like in mission impossible.</p>
<p><img src="http://i.imgur.com/upMxFQh.gif" alt="Self-destructing"></p>
]]></description>
    </item>
    
    <item>
	    <title>My poetry generator passed the Turing Test</title>
	    <image>https://schollz.com/img/the_archive_pg31-1.jpg</image>
      <link>/tinker/poetry/</link>
      <pubDate>Sat, 25 Apr 2015 09:43:35 -0600</pubDate>
      
      <guid>/tinker/poetry/</guid>
      <description><![CDATA[<p>The real Turing Test of this AI was to get it accepted to a literary journal, which was accomplished - this poetry was successfully accepted into a literary journal at a prestigious university. The story is below and the code is at the bottom for those who want to make their own!</p>
<h2 id="what-do-you-think-of-the-following-poem">What do you think of the following poem?</h2>
<div id="poem">
<blockquote>
<p>
<strong><em>Orange Light</em></strong><br /> <em>I conduct myself in a windy manner because I am</em><br /> <em> drunk and enchanted in this field.</em><br /> <em> The oxygen around my head is rabid</em><br /> <em> and filled with orange light</em><br /> <em> like a equinoctial tiger to its flesh.</em><br /> <em> My heart moves violently</em><br /> <em> on this neon ship.</em>
</p>
<p>
<em>I promise as I were a rotting ghost</em><br /> <em> forced half-open in love</em><br /> <em> in front of the gray agony of the darkness</em><br /> <em> and decaying droplets of acidulous gold.</em>
</p>
<p>
<em>I reply, only fear and geology are the</em><br /> <em> leaves of belligerence.</em>
</p>
<p>
<em>I’d do it for the geology</em><br /> <em> and I’d do it for the fear of your response.</em>
</p>
</blockquote>
</div>
<p>If you answered anything other than <em>“That’s not a poem!”</em> to that question, then you probably believe more or less that it <em>is</em> a poem, and by some standards then the artificial intelligence that generated this poem once again passed the Turing Test.</p>
<h2 id="turing-test-whats-that">Turing test, what’s that?</h2>
<p>The Turing test is a test of the ability of a machine to exhibit intelligence that is indistinguishable from a human. It can exist in many varieties - chat bots, intelligence bots, artist bots. This poetry generator is a very simple type of artificial intelligence, yet it is indistinguishable from any sort of poetry you may encounter from other humans.</p>
<p>There have been generators among the years that are capable of passing Turing tests. In 2005, <a href="http://pdos.csail.mit.edu/scigen/" target="_blank" >students wrote a generator for academic CS papers</a>. One of the generated papers was accepted to a conference before conference organizers realized it was a joke. There is also <a href="http://www.elsewhere.org/pomo/" target="_blank" >a great post-modernism generator</a> developed by Andrew Bulhak and Josh Larios, which demonstrates the ability to generate cohesive but incoherent post-modernist literature academic papers.</p>
<h2 id="how-does-the-poetry-generator-work">How does the poetry generator work?</h2>
<p>This Poetry generator uses a <a href="https://en.wikipedia.org/wiki/Context-free_grammar" target="_blank" >Context-free grammar</a> using the notation of <a href="https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form" target="_blank" >Backus-Naur Form</a>. Context-free grammar systems are a generalized system of formal grammar defined by production rules which allow sentences to be recursively built from smaller phrases. The formalism was developed by <a href="https://en.wikipedia.org/wiki/Noam_Chomsky" target="_blank" >Noam Chomskey</a> in the 1950’s.</p>
<p>Essentially this poetry generator works by having the poem dissected into smaller components: stanzas, lines, phrases, then verbs, adjectives, and nouns. When a call to create a poem is made, then it randomly selects components of the poem and recursively generates each of those. For example, a generated title may look something like this:</p>
<pre><code>*title*=A *fruit*
*fruit*=grape|apple|orange|banana|cherry|mango|kiwi|tomato|lemon|fruit
</code></pre>
<p>In that case the title is generated as “A ” and then it looks up and selects one of the possible “” words to finish it before it returns “A grape” or something similar.</p>
<h2 id="does-this-computer-generated-poetry-pass-the-turing-test">Does this computer-generated poetry pass the Turing test?</h2>
<p>I began by submitting my poetry to <a href="http://www.your-poetry.com/index.php" target="_blank" >your-poetry.com</a> which was a popular poetry website of the time (in 2010). I never conferred that these poems were computer-generated. I published under the name <a href="http://www.your-poetry.com/modules.php?name=Your_Account&amp;op=userinfo&amp;username=antikythera" target="_blank" >Antikythera</a>. The responses I received were, for the most part, quite flattering. Here are some of the responses I got:</p>
<blockquote>
<p><em>“Many layers to this piece, it demands a second and even third reading. I like the challenge you’ve set out here, well done.”</em></p>
</blockquote>
<blockquote>
<p><em>&ldquo;I like this! Its like it is straight from the pages of a mystical story,filled with delightful colours and wonderful landscapes.&rdquo;</em></p>
</blockquote>
<blockquote>
<p><em>“I like you style of just giving the reader a small taste of what you are expressing. It opens up the thought process and gives us something to ponder without fully knowing the subject. Nice!”</em></p>
</blockquote>
<blockquote>
<p><em>“What a wonderful piece of writing. you paint a vivid picture and I love the picture you have painted here. well done.”</em></p>
</blockquote>
<blockquote>
<p><em>“Its like you just sang a beautiful song…”</em></p>
</blockquote>
<blockquote>
<p><em>“you certainly know how to make words jump off the page, well written, well done.”</em></p>
</blockquote>
<p>The negative criticism I received typically found that my poems were incomprehensible or nonsensical, such as the following:</p>
<blockquote>
<p><em>“Sorry to say you have a very strange way of expressing yourself and this poem I have really not understood.”</em></p>
</blockquote>
<p>The overwhelming positive responses compelled me to use a more stringent test of authenticity - seeing whether this computer-generated poetry could get published in a poetry or literary journal.</p>
<h2 id="final-test-getting-computer-generated-poetry-published">Final test: Getting computer-generated poetry published</h2>
<p>As many poets often do, I tried submitting my poetry to several places. When I submitted these poems to publications I never specified that they were computer generated. I submitted to the Memoir Journal (now defunct) and First Writer Poetry contest but both rejected the poetry without comment. I then turned to local sources and submitted to The Archive, one of the oldest continuously published literary magazines in the United States and the oldest student publication at <a href="https://dukearchive.wordpress.com/" target="_blank" >Duke University</a>.</p>
<p>The Archive agreed to publish one of my poems! The email chain is below. I actually submitted 26 poems (one for each letter of the alphabet) and selected a good one to publish.</p>
<p><img src="/img/the_archive_email.jpg" alt="the_archive_email"></p>
<p>The Archive published a great poem and selected an image of a bulldozer to be accompanied with it. The bulldozer is interesting, I think that they implied that the poem&rsquo;s central theme is something about the destruction of the environment? The poem and art is shown below:</p>
<p><img src="/img/the_archive_pg31-1.jpg" alt="the_archive_pg31"></p>
<p><img src="/img/the_archive_pg32.jpg" alt="the_archive_pg32"></p>
<p><strong>In conclusion</strong> it seems very possible to create an artificial intelligence to do specialized tasks, like writing poetry, that can sufficiently pass as a human being. Perhaps in the future we will need to question the source of creative materials to determine whether they are indeed human or machine made.</p>
<h2 id="interested-in-making-your-own-poem"><em>Interested in making your own poem?</em></h2>
<p>The source code for my poetry generator (now implemented in Python instead of Java) is open-source and available. Click the button below to generate your own poem, or check out the source code on <a href="https://github.com/schollz/poetry-generator" target="_blank" >Github</a>.</p>
<p>Go to  <a href="http://www.poetrygenerator.ninja" target="_blank" >poetrygenerator.ninja</a> to try generating your own poem!</p>
]]></description>
    </item>
    
    <item>
	    <title>Raspberry Pi AI</title>
	    <image>https://schollz.com/img/rpiai.jpg</image>
      <link>/tinker/ai-bot/</link>
      <pubDate>Wed, 05 Feb 2014 07:55:42 -0700</pubDate>
      
      <guid>/tinker/ai-bot/</guid>
      <description><![CDATA[<p>I create a fully functional AI out of a Raspberry Pi - including voice recognition, facial recognition, and text-to-speech.</p>
<h2 id="choose-the-hardware">Choose the hardware</h2>
<p>Here is the list of my hardware components (as of 02/05/14):</p>
<ul>
<li><a href="http://www.amazon.com/RASPBERRY-MODEL-756-8308-Raspberry-Pi/dp/B009SQQF9C" target="_blank" >Raspberry Pi Model</a></li>
<li><a href="http://www.alliedelec.com/search/productdetail.aspx?SKU=70280250" target="_blank" >Raspberry Pi Camera</a></li>
<li><a href="http://www.amazon.com/gp/product/B0052SBAEU/ref=oh_details_o03_s00_i00?ie=UTF8&amp;psc=1" target="_blank" >USB microphone</a></li>
<li><a href="http://www.amazon.com/X10-R-F-PC-Transceiver-CM19A/dp/B0027RMIAO/ref=sr_1_1?s=electronics&amp;ie=UTF8&amp;qid=1392036372&amp;sr=1-1&amp;keywords=cm19a" target="_blank" >CM19A RF Transceiver</a></li>
<li>8GB SD Card</li>
<li>USB 2.0 10-port Hub</li>
<li><a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16833315091" target="_blank" >USB WIFI adaptor</a></li>
<li>Computer speakers</li>
<li>HDMI cable</li>
</ul>
<p>And here is the explanation: I will be using a <a href="http://www.amazon.com/RASPBERRY-MODEL-756-8308-Raspberry-Pi/dp/B009SQQF9C" target="_blank" >Raspberry Pi Model B</a> for the “computer” and using <a href="http://www.raspbian.org/" target="_blank" >Raspbian linux</a> for the OS. There are plenty of other places that show you <a href="http://www.andrewmunsell.com/blog/getting-started-raspberry-pi-install-raspbian" target="_blank" >how to get started</a> so I am going to skip that here. For visuals I am using the <a href="http://www.alliedelec.com/search/productdetail.aspx?SKU=70280250" target="_blank" >Raspberry Pi Camera</a> which is nicely mounted in <a href="http://www.storenvy.com/products/4062416-raspberry-pi-and-camera-enclosure" target="_blank" >a custom made enclosure</a>. For audio I am using a simple microphone (<a href="http://www.alliedelec.com/search/productdetail.aspx?SKU=70280250" target="_blank" >this one is nice</a>because it blinks when it is recording and you can turn it off - and it is cheap!). I already have <a href="http://www.amazon.com/X10-TM751-Wireless-Transceiver-Module/dp/B000BT7XUK/ref=sr_1_2?s=electronics&amp;ie=UTF8&amp;qid=1391605702&amp;sr=1-2&amp;keywords=cm19a" target="_blank" >X10 transcievers</a> and was planning on using my old CM15A until it just up and died almost exactly two years after I bought it. Sidenote: X10 is terrible about helping to fix these things and a replacement is not cheap to find, so avoid X10 if you can. Since I am stuck using X10 I decided to get the cheap <a href="http://www.amazon.com/X10-R-F-PC-Transceiver-CM19A/dp/B0027RMIAO/ref=sr_1_1?s=electronics&amp;ie=UTF8&amp;qid=1391605702&amp;sr=1-1&amp;keywords=cm19a" target="_blank" >CM19A USB RF transceiver</a>which works well although I can’t use power line codes with it. Note - if you want cheap X10 stuff, check out <a href="http://www.ebay.com/sch/i.html?_sacat=0&amp;_nkw=x10&#43;transceiver&amp;_frs=1" target="_blank" >eBay</a>.</p>
<h2 id="choosing-the-software">Choosing the software</h2>
<p>Given an Linux environment, there are many choices of a coding language. I would normally choose of the following (ordered according to my favorite): Perl, Java, C++, PHP, Basic. However, one must keep in mind that the RPi is 700MHz processor with 512MB RAM. Thus, it is not something that I can program from the bottom-up because even a simple image recognition program that I could write would tear up the processor (I am too dumb and lazy to write on that is memory efficient). Thus, to alleviate heavy lifting I would turn to online APIs.</p>
<p>Thus, my choice of a programming language is one which has a number of great APIs and wrappers available. Python was presented to me as a great choice, so I went with that. There are many APIs available for Python. However, everything I am about to show could just as easily been done with Java. Also, I had never programmed with Python before so this was going to be a good opportunity for me to learn.</p>
<h2 id="see-tts-and-stt-in-action">See TTS and STT in action!</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/kQMz4GFU_9o" frameborder="0" allowfullscreen></iframe>
<p>Now how does it work? Lets find out!</p>
<h2 id="text-to-speech">Text-to-speech</h2>
<p>I found two options for TTS: <a href="http://packages.debian.org/unstable/espeak" target="_blank" >espeak</a> (from Debian) or using the <a href="http://techcrunch.com/2009/12/14/the-unofficial-google-text-to-speech-api/" target="_blank" >Google TTS</a>. First I tried using espeak, which was easy to get going. However it suffers from a drawback that when it speaks long sentence it slows down and sounds like a Dalek (<a href="http://www.raspberrypi.org/phpBB3/viewtopic.php?f=38&amp;t=47942" target="_blank" >documented here</a>). Since I would be saying things that are &gt;100 words this was not a good solution, but maybe useful for you. Google TTS works great and is easy to get going, however it suffers from a drawback that you can only say 100 characters at a time. However the speech clarity is nice so I decided to go with this. It is fairly simple to use, the main command is just:</p>
<pre tabindex="0"><code>mpg123 -a hw:YOURALSANUMBER -q &#39;http://translate.google.com/translate_tts?tl=en&amp;amp;q=WORD1%20WORD2%ETC&#39;
</code></pre><p>You&rsquo;ll have to install and configure mpg123 to work (there are other places to figure that out, let me know if you need help). Essentially the next part was to write some code that would split the sentences into 100 character sentences. This is accomplished below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-1-1"><a class="lnlinks" href="#hl-1-1"> 1</a></span><span class="cl"><span class="k">def</span> <span class="nf">saySomething</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span><span class="n">language</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-1-2"><a class="lnlinks" href="#hl-1-2"> 2</a></span><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;speaking &#34;</span> <span class="o">+</span> <span class="n">language</span>
</span></span><span class="line"><span class="ln" id="hl-1-3"><a class="lnlinks" href="#hl-1-3"> 3</a></span><span class="cl">    <span class="n">words</span> <span class="o">=</span> <span class="n">txt</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-1-4"><a class="lnlinks" href="#hl-1-4"> 4</a></span><span class="cl">    <span class="n">numWords</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">words</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-5"><a class="lnlinks" href="#hl-1-5"> 5</a></span><span class="cl">    <span class="n">sentences</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-1-6"><a class="lnlinks" href="#hl-1-6"> 6</a></span><span class="cl">    <span class="n">curSentence</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-1-7"><a class="lnlinks" href="#hl-1-7"> 7</a></span><span class="cl">    <span class="n">curCharacters</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-1-8"><a class="lnlinks" href="#hl-1-8"> 8</a></span><span class="cl">    <span class="n">curWord</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-1-9"><a class="lnlinks" href="#hl-1-9"> 9</a></span><span class="cl">    <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">words</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-1-10"><a class="lnlinks" href="#hl-1-10">10</a></span><span class="cl">        <span class="k">if</span> <span class="n">curCharacters</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">word</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="o">&lt;</span><span class="mi">100</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-1-11"><a class="lnlinks" href="#hl-1-11">11</a></span><span class="cl">            <span class="n">sentences</span> <span class="o">=</span> <span class="n">sentences</span><span class="o">+</span><span class="s1">&#39;%20&#39;</span><span class="o">+</span><span class="n">word</span>
</span></span><span class="line"><span class="ln" id="hl-1-12"><a class="lnlinks" href="#hl-1-12">12</a></span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-1-13"><a class="lnlinks" href="#hl-1-13">13</a></span><span class="cl">            <span class="n">curSentence</span> <span class="o">=</span> <span class="n">curSentence</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-14"><a class="lnlinks" href="#hl-1-14">14</a></span><span class="cl">            <span class="n">sentences</span> <span class="o">=</span> <span class="n">sentences</span> <span class="o">+</span> <span class="s2">&#34;111&#34;</span> <span class="o">+</span> <span class="n">word</span>
</span></span><span class="line"><span class="ln" id="hl-1-15"><a class="lnlinks" href="#hl-1-15">15</a></span><span class="cl">            <span class="n">curCharacters</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-1-16"><a class="lnlinks" href="#hl-1-16">16</a></span><span class="cl">        <span class="n">curCharacters</span> <span class="o">=</span> <span class="n">curCharacters</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">word</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-17"><a class="lnlinks" href="#hl-1-17">17</a></span><span class="cl">        <span class="n">curWord</span> <span class="o">=</span> <span class="n">curWord</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln" id="hl-1-18"><a class="lnlinks" href="#hl-1-18">18</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-1-19"><a class="lnlinks" href="#hl-1-19">19</a></span><span class="cl">    <span class="n">feedTxt</span> <span class="o">=</span> <span class="n">sentences</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;111&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-20"><a class="lnlinks" href="#hl-1-20">20</a></span><span class="cl">    <span class="k">for</span> <span class="n">sentence</span> <span class="ow">in</span> <span class="n">feedTxt</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-1-21"><a class="lnlinks" href="#hl-1-21">21</a></span><span class="cl">    <span class="n">sentence</span> <span class="o">=</span> <span class="n">sentence</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&#34;&#39;&#34;</span><span class="p">,</span><span class="s2">&#34;%27&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-1-22"><a class="lnlinks" href="#hl-1-22">22</a></span><span class="cl">    <span class="nb">print</span> <span class="n">sentence</span>
</span></span><span class="line"><span class="ln" id="hl-1-23"><a class="lnlinks" href="#hl-1-23">23</a></span><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">&#34;mpg123 -a hw:1 -q &#39;http://translate.google.com/translate_tts?tl=&#34;</span><span class="o">+</span><span class="n">language</span><span class="o">+</span><span class="s2">&#34;&amp;q=&#34;</span> <span class="o">+</span> <span class="n">sentence</span> <span class="o">+</span> <span class="s2">&#34;&#39;&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="speech-to-text">Speech-to-text</h2>
<p>The STT is pretty simple as it consists of three steps: activation, acquisition, and translation. Activation can be accomplished via a &ldquo;key press&rdquo; but I much rather use voice activation. Assuming you live in a normally quiet atmosphere, it is perfectly practical (and easy) to calculate the root mean square noise (RMS) and activate upon a given threshold. You can set the threshold by acquiring a distribution and looking at standard deviations, or you can just choose a number. Either way you can look at typical RMS values for your given mic/environment using the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-2-1"><a class="lnlinks" href="#hl-2-1"> 1</a></span><span class="cl"><span class="kn">import</span> <span class="nn">audioop</span>
</span></span><span class="line"><span class="ln" id="hl-2-2"><a class="lnlinks" href="#hl-2-2"> 2</a></span><span class="cl"><span class="kn">import</span> <span class="nn">pyaudio</span>
</span></span><span class="line"><span class="ln" id="hl-2-3"><a class="lnlinks" href="#hl-2-3"> 3</a></span><span class="cl"><span class="n">rms</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln" id="hl-2-4"><a class="lnlinks" href="#hl-2-4"> 4</a></span><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">100</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-2-5"><a class="lnlinks" href="#hl-2-5"> 5</a></span><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">pyaudio</span><span class="o">.</span><span class="n">PyAudio</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-6"><a class="lnlinks" href="#hl-2-6"> 6</a></span><span class="cl">    <span class="n">stream</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="n">pyaudio</span><span class="o">.</span><span class="n">paInt16</span><span class="p">,</span><span class="n">channels</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">rate</span><span class="o">=</span><span class="mi">44100</span><span class="p">,</span><span class="nb">input</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span><span class="n">frames_per_buffer</span><span class="o">=</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-7"><a class="lnlinks" href="#hl-2-7"> 7</a></span><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">stream</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-8"><a class="lnlinks" href="#hl-2-8"> 8</a></span><span class="cl">    <span class="n">rmsTemp</span> <span class="o">=</span> <span class="n">audioop</span><span class="o">.</span><span class="n">rms</span><span class="p">(</span><span class="n">data</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-2-9"><a class="lnlinks" href="#hl-2-9"> 9</a></span><span class="cl">    <span class="nb">print</span> <span class="n">rmsTemp</span>
</span></span><span class="line"><span class="ln" id="hl-2-10"><a class="lnlinks" href="#hl-2-10">10</a></span><span class="cl"><span class="n">stream</span><span class="o">.</span><span class="n">stop_stream</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-11"><a class="lnlinks" href="#hl-2-11">11</a></span><span class="cl"><span class="n">stream</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-2-12"><a class="lnlinks" href="#hl-2-12">12</a></span><span class="cl"><span class="n">p</span><span class="o">.</span><span class="n">terminate</span><span class="p">()</span>
</span></span></code></pre></div><p>I&rsquo;ve set my threshold to 1050 (an arbitrary value, you should find your own). Now then the first major subroutine of the AI can be set - the listening function. This will essentially run infinitely and its nice to allow this to run as a thread (it may be needed later). This is the basic code for the activation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-3-1"><a class="lnlinks" href="#hl-3-1"> 1</a></span><span class="cl"><span class="kn">import</span> <span class="nn">audioop</span>
</span></span><span class="line"><span class="ln" id="hl-3-2"><a class="lnlinks" href="#hl-3-2"> 2</a></span><span class="cl"><span class="kn">import</span> <span class="nn">pyaudio</span>
</span></span><span class="line"><span class="ln" id="hl-3-3"><a class="lnlinks" href="#hl-3-3"> 3</a></span><span class="cl"><span class="k">def</span> <span class="nf">listenToSurroundings</span><span class="p">(</span><span class="n">threadName</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-3-4"><a class="lnlinks" href="#hl-3-4"> 4</a></span><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-3-5"><a class="lnlinks" href="#hl-3-5"> 5</a></span><span class="cl">        <span class="nb">print</span> <span class="s2">&#34;Started listening on thread </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">threadName</span>
</span></span><span class="line"><span class="ln" id="hl-3-6"><a class="lnlinks" href="#hl-3-6"> 6</a></span><span class="cl">        <span class="n">chunk</span> <span class="o">=</span> <span class="mi">1024</span>
</span></span><span class="line"><span class="ln" id="hl-3-7"><a class="lnlinks" href="#hl-3-7"> 7</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-8"><a class="lnlinks" href="#hl-3-8"> 8</a></span><span class="cl">        <span class="n">volumeThreshold</span> <span class="o">=</span> <span class="mi">1050</span>
</span></span><span class="line"><span class="ln" id="hl-3-9"><a class="lnlinks" href="#hl-3-9"> 9</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-10"><a class="lnlinks" href="#hl-3-10">10</a></span><span class="cl">        <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-3-11"><a class="lnlinks" href="#hl-3-11">11</a></span><span class="cl">            <span class="nb">print</span> <span class="s2">&#34;Starting listening stream&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-3-12"><a class="lnlinks" href="#hl-3-12">12</a></span><span class="cl">            <span class="n">rmsTemp</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-3-13"><a class="lnlinks" href="#hl-3-13">13</a></span><span class="cl">            <span class="n">p</span> <span class="o">=</span> <span class="n">pyaudio</span><span class="o">.</span><span class="n">PyAudio</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-3-14"><a class="lnlinks" href="#hl-3-14">14</a></span><span class="cl">            <span class="n">stream</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="n">pyaudio</span><span class="o">.</span><span class="n">paInt16</span><span class="p">,</span><span class="n">channels</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="n">rate</span><span class="o">=</span><span class="mi">16000</span><span class="p">,</span><span class="nb">input</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span><span class="n">frames_per_buffer</span><span class="o">=</span><span class="n">chunk</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-15"><a class="lnlinks" href="#hl-3-15">15</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-3-16"><a class="lnlinks" href="#hl-3-16">16</a></span><span class="cl">            <span class="k">while</span> <span class="n">rmsTemp</span> <span class="o">&lt;</span> <span class="n">volumeThreshold</span>
</span></span><span class="line"><span class="ln" id="hl-3-17"><a class="lnlinks" href="#hl-3-17">17</a></span><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">stream</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-18"><a class="lnlinks" href="#hl-3-18">18</a></span><span class="cl">                <span class="n">rmsTemp</span> <span class="o">=</span> <span class="n">audioop</span><span class="o">.</span><span class="n">rms</span><span class="p">(</span><span class="n">data</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-19"><a class="lnlinks" href="#hl-3-19">19</a></span><span class="cl">            <span class="n">stream</span><span class="o">.</span><span class="n">stop_stream</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-3-20"><a class="lnlinks" href="#hl-3-20">20</a></span><span class="cl">            <span class="n">stream</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-3-21"><a class="lnlinks" href="#hl-3-21">21</a></span><span class="cl">            <span class="n">p</span><span class="o">.</span><span class="n">terminate</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-3-22"><a class="lnlinks" href="#hl-3-22">22</a></span><span class="cl">            <span class="n">output</span> <span class="o">=</span> <span class="n">getUsersVoice</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-23"><a class="lnlinks" href="#hl-3-23">23</a></span><span class="cl">            <span class="n">processInput</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-3-24"><a class="lnlinks" href="#hl-3-24">24</a></span><span class="cl">    <span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-3-25"><a class="lnlinks" href="#hl-3-25">25</a></span><span class="cl">        <span class="kn">import</span> <span class="nn">traceback</span>
</span></span><span class="line"><span class="ln" id="hl-3-26"><a class="lnlinks" href="#hl-3-26">26</a></span><span class="cl">                <span class="nb">print</span> <span class="n">traceback</span><span class="o">.</span><span class="n">format_exc</span><span class="p">()</span>
</span></span></code></pre></div><p>The try/except block is to catch errors, especially useful for the debug stage. The aquisition and translation stages are done in another subroutine, <code>getUsersVoice</code>. This is a pretty simple code - it will first beep to notify that aquisition has begun. Then it will use <code>arecord</code> to record the audio for a given amount of time. It will beep when finished. Then it will send the text to the Google Speech API. For this last step I use a separate bash file, <code>parseVoiceText.sh</code> just because there are so many quotations. Here is the code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-4-1"><a class="lnlinks" href="#hl-4-1"> 1</a></span><span class="cl"><span class="k">def</span> <span class="nf">getUsersVoice</span><span class="p">(</span><span class="n">speakingTime</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-4-2"><a class="lnlinks" href="#hl-4-2"> 2</a></span><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">&#34;mpg123 -a hw:YOURALSAPLAYBACK YOURBEEPSOUND.mp3 &gt; /dev/null 2&gt;&amp;1 &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-3"><a class="lnlinks" href="#hl-4-3"> 3</a></span><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">&#34;arecord -D plughw:YOURALSARECORDING -f cd -t wav -d </span><span class="si">%d</span><span class="s2"> -r 16000 | flac - -f --best --sample-rate 16000 -o out.flac&gt; /dev/null 2&gt;&amp;1 &#34;</span> <span class="o">%</span> <span class="n">speakingTime</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-4"><a class="lnlinks" href="#hl-4-4"> 4</a></span><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">&#34;mpg123 -a hw:YOURALSAPLAYBACK YOURBEEPSOUND.mp3 &gt; /dev/null 2&gt;&amp;1 &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-5"><a class="lnlinks" href="#hl-4-5"> 5</a></span><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">&#34;./parseVoiceText.sh &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-4-6"><a class="lnlinks" href="#hl-4-6"> 6</a></span><span class="cl">    <span class="n">output</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-7"><a class="lnlinks" href="#hl-4-7"> 7</a></span><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">&#39;txt.out&#39;</span><span class="p">,</span><span class="s1">&#39;r&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-4-8"><a class="lnlinks" href="#hl-4-8"> 8</a></span><span class="cl">        <span class="n">output</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-4-9"><a class="lnlinks" href="#hl-4-9"> 9</a></span><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;output:&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-4-10"><a class="lnlinks" href="#hl-4-10">10</a></span><span class="cl">    <span class="nb">print</span> <span class="n">output</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-4-11"><a class="lnlinks" href="#hl-4-11">11</a></span><span class="cl">    <span class="n">theOutput</span> <span class="o">=</span> <span class="n">output</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-4-12"><a class="lnlinks" href="#hl-4-12">12</a></span><span class="cl">    <span class="k">return</span> <span class="n">theOutput</span>
</span></span></code></pre></div><p>And the bash file:</p>
<pre tabindex="0"><code># parseVoiceText.sh
wget -O - -o /dev/null --post-file out.flac --header=&#34;Content-Type: audio/x-flac; rate=16000&#34; http://www.google.com/speech-api/v1/recognize?lang=en | sed -e &#39;s/[{}]/&#39;&#39;/g&#39;| awk -v k=&#34;text&#34; &#39;{n=split($0,a,&#34;,&#34;); for (i=1; i&lt;=n; i++) print a[i]; exit }&#39; | awk -F: &#39;NR==3 { print $3; exit }&#39; &gt; txt.out
</code></pre><p>As you probably noticed, I didn&rsquo;t tell you about processInput(). That&rsquo;s going to be the main function to handle events. I am currently fleshing that out and will post back when I have some more on that.</p>
<h2 id="how-to-do-facial-recognition">How to do facial recognition</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/186RtLfy7lM" frameborder="0" allowfullscreen></iframe>
<p>Now how does it work? Lets find out!</p>
<p>Once again, this will be short and sweet. The code here is the most simple, it does not save any of the processed images. If people are interested in that I can post code that will save images of the faces with their features labeled, but I am excluding that now for clarity. Here is the basic code:</p>
<pre tabindex="0"><code>def recognizeFace():
        # Take picture and save to webserver
        filename = &#34;/var/www/temp.jpg&#34;
        subprocess.call(&#34;sudo raspistill -w &#34;+ str(saveWidth) +&#34; -h &#34;+ str(saveHeight) + &#34; -t 1 -n -vf -e &#34; + fileType + &#34; -q 15 -o %s&#34; % filename, shell=True)
        print &#34;Captured image: %s&#34; % filename

        # Send the picture to the SkyBio Api
        theFileLocation =  &#34;http://YOURPUBLICIP/temp.jpg&#34;
        client = FaceClient(YOURSKYBIOAPI,YOURSKYBIOAPISECRET)
        photo = client.faces_recognize(&#39;all&#39;,theFileLocation, namespace = &#39;YOURSKYBIONAMESPACE&#39;)

        # Number of people in photo
        numFaces = len(photo[&#39;photos&#39;][0][&#39;tags&#39;])
        print &#34;Detected &#34; + str(numFaces) + &#34; faces.&#34;

        # Go through each face
        theSpeech = &#34;&#34;
        while iii2: # proxy for if something happened
                print theSpeech
                saySomething(theSpeech,&#34;en&#34;)
</code></pre><p>Now for the explanation. Firstly, this code uses <a href="https://www.skybiometry.com/" target="_blank" >this API which gives 5000 calls/month for free</a> for facial recognition. It works well if you train it well. I will defer the explanation of the training to <a href="https://github.com/Liuftvafas/python-face-client" target="_blank" >others who wrote beautifully on this</a>.</p>
<p>For this API to work, though, you have to send the picture from a location on the Web. There are a few ways to do this. If you already have a webserver you can just SCP it over your VPN network. I went for a simpler solution. I hosted the image on my RPi using an apache webserver - <a href="http://www.jeremymorgan.com/tutorials/raspberry-pi/how-to-raspberry-pi-web-server/" target="_blank" >instructions are here</a>. Don’t forget to forward port 80. You can get your public IP address <a href="http://www.whatismyip.com/" target="_blank" >from whatismyip</a>. Now you can use the /var/www/ as your web server folder, and you can pass those images to SkyBio. <em><a href="http://www.javascriptkit.com/howto/htaccess3.shtml" target="_blank" >Note: I suggest password protecting your folder using .htaccess</a></em></p>
<p>This code just finds each face. However, SkyBio tries to find all sorts of other things like whether you have glasses or not. You can probe all these things (and their confidence levels) with the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-7-1"><a class="lnlinks" href="#hl-7-1">1</a></span><span class="cl"><span class="n">skybio_metrics</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;eyes&#39;</span><span class="p">,</span><span class="s1">&#39;sadness&#39;</span><span class="p">,</span><span class="s1">&#39;mood&#39;</span><span class="p">,</span><span class="s1">&#39;glasses&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-7-2"><a class="lnlinks" href="#hl-7-2">2</a></span><span class="cl"><span class="k">for</span> <span class="n">metric</span> <span class="ow">in</span> <span class="n">skybio_metrics</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-7-3"><a class="lnlinks" href="#hl-7-3">3</a></span><span class="cl">    <span class="n">val</span> <span class="o">=</span> <span class="n">photo</span><span class="p">[</span><span class="s1">&#39;photos&#39;</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">&#39;tags&#39;</span><span class="p">][</span><span class="n">iii</span><span class="p">][</span><span class="s1">&#39;attributes&#39;</span><span class="p">][</span><span class="n">metric</span><span class="p">][</span><span class="s1">&#39;value&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-7-4"><a class="lnlinks" href="#hl-7-4">4</a></span><span class="cl">    <span class="n">conf</span> <span class="o">=</span> <span class="n">photo</span><span class="p">[</span><span class="s1">&#39;photos&#39;</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s1">&#39;tags&#39;</span><span class="p">][</span><span class="n">iii</span><span class="p">][</span><span class="s1">&#39;attributes&#39;</span><span class="p">][</span><span class="n">metric</span><span class="p">][</span><span class="s1">&#39;confidence&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-7-5"><a class="lnlinks" href="#hl-7-5">5</a></span><span class="cl">    <span class="k">if</span> <span class="n">conf</span> <span class="o">&gt;</span> <span class="mi">20</span><span class="p">:</span>
</span></span><span class="line"><span class="ln" id="hl-7-6"><a class="lnlinks" href="#hl-7-6">6</a></span><span class="cl">        <span class="c1"># do something with metric</span>
</span></span></code></pre></div><p>Does this work? See for yourself (this output comes from a slightly modified version of above that prints the metric over the face):</p>
<p><img src="/img/detected.jpg" alt="Detected my face!"></p>
<h2 id="adding-a-face-to-the-ai">Adding a face to the AI</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/_w9M08eoKMU" frameborder="0" allowfullscreen></iframe>
<p>Now how does it work? Lets find out!</p>
<p>So for a face, I thought I would try making some sort of realtime voice animating system. I made a very very simple one in python. Here is an example of what it looks like and sounds like:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/A9YsegD3Pwk" frameborder="0" allowfullscreen></iframe>
<p>The source code is very simple. Get the whole code at my <a href="https://github.com/schollz/rpi_ai" target="_blank" >github page</a> (the following excludes the saySomething function which is part of <code>mouth_function</code>. Basically the following code opens a process to say something and at the same time tries to animate it with an open/closing mouth. The timing between the mouth open and mouth closed comes from an average timing I got from recording the Google TTS and recording how much time it takes to say a word and the amount of time between words. The other trick is to count the number of syllables in a word.</p>
<p>Python has a fast way of doing this using <code>nltk</code> (coded below).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln" id="hl-8-1"><a class="lnlinks" href="#hl-8-1"> 1</a></span><span class="cl"><span class="kn">import</span> <span class="nn">pygame</span><span class="o">,</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">time</span><span class="o">,</span> <span class="nn">random</span>
</span></span><span class="line"><span class="ln" id="hl-8-2"><a class="lnlinks" href="#hl-8-2"> 2</a></span><span class="cl"><span class="kn">from</span> <span class="nn">pygame.locals</span> <span class="kn">import</span> <span class="o">*</span>
</span></span><span class="line"><span class="ln" id="hl-8-3"><a class="lnlinks" href="#hl-8-3"> 3</a></span><span class="cl"><span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="o">*</span>
</span></span><span class="line"><span class="ln" id="hl-8-4"><a class="lnlinks" href="#hl-8-4"> 4</a></span><span class="cl"><span class="kn">import</span> <span class="nn">curses</span>
</span></span><span class="line"><span class="ln" id="hl-8-5"><a class="lnlinks" href="#hl-8-5"> 5</a></span><span class="cl"><span class="kn">from</span> <span class="nn">curses.ascii</span> <span class="kn">import</span> <span class="n">isdigit</span>
</span></span><span class="line"><span class="ln" id="hl-8-6"><a class="lnlinks" href="#hl-8-6"> 6</a></span><span class="cl"><span class="kn">import</span> <span class="nn">nltk</span>
</span></span><span class="line"><span class="ln" id="hl-8-7"><a class="lnlinks" href="#hl-8-7"> 7</a></span><span class="cl"><span class="kn">from</span> <span class="nn">nltk.corpus</span> <span class="kn">import</span> <span class="n">cmudict</span>
</span></span><span class="line"><span class="ln" id="hl-8-8"><a class="lnlinks" href="#hl-8-8"> 8</a></span><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="ln" id="hl-8-9"><a class="lnlinks" href="#hl-8-9"> 9</a></span><span class="cl"><span class="kn">import</span> <span class="nn">thread</span>
</span></span><span class="line"><span class="ln" id="hl-8-10"><a class="lnlinks" href="#hl-8-10">10</a></span><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="ln" id="hl-8-11"><a class="lnlinks" href="#hl-8-11">11</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-12"><a class="lnlinks" href="#hl-8-12">12</a></span><span class="cl"><span class="n">d</span> <span class="o">=</span> <span class="n">cmudict</span><span class="o">.</span><span class="n">dict</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-13"><a class="lnlinks" href="#hl-8-13">13</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-14"><a class="lnlinks" href="#hl-8-14">14</a></span><span class="cl"><span class="k">def</span> <span class="nf">nsyl</span><span class="p">(</span><span class="n">word</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-8-15"><a class="lnlinks" href="#hl-8-15">15</a></span><span class="cl">        <span class="k">return</span> <span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">y</span> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">x</span> <span class="k">if</span> <span class="n">isdigit</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">d</span><span class="p">[</span><span class="n">word</span><span class="o">.</span><span class="n">lower</span><span class="p">()]]</span>
</span></span><span class="line"><span class="ln" id="hl-8-16"><a class="lnlinks" href="#hl-8-16">16</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-17"><a class="lnlinks" href="#hl-8-17">17</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-18"><a class="lnlinks" href="#hl-8-18">18</a></span><span class="cl"><span class="n">windowSurface</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="mi">500</span><span class="p">,</span> <span class="mi">400</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">32</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-19"><a class="lnlinks" href="#hl-8-19">19</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_caption</span><span class="p">(</span><span class="s2">&#34;Bounce&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-20"><a class="lnlinks" href="#hl-8-20">20</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-21"><a class="lnlinks" href="#hl-8-21">21</a></span><span class="cl"><span class="n">BLACK</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-22"><a class="lnlinks" href="#hl-8-22">22</a></span><span class="cl"><span class="n">WHITE</span> <span class="o">=</span> <span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-23"><a class="lnlinks" href="#hl-8-23">23</a></span><span class="cl"><span class="n">RED</span> <span class="o">=</span> <span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-24"><a class="lnlinks" href="#hl-8-24">24</a></span><span class="cl"><span class="n">GREEN</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-25"><a class="lnlinks" href="#hl-8-25">25</a></span><span class="cl"><span class="n">BLUE</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-26"><a class="lnlinks" href="#hl-8-26">26</a></span><span class="cl"><span class="n">YELLOW</span> <span class="o">=</span> <span class="p">(</span><span class="mi">255</span><span class="p">,</span><span class="mi">255</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-27"><a class="lnlinks" href="#hl-8-27">27</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-28"><a class="lnlinks" href="#hl-8-28">28</a></span><span class="cl"><span class="n">info</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">Info</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-29"><a class="lnlinks" href="#hl-8-29">29</a></span><span class="cl"><span class="n">sw</span> <span class="o">=</span> <span class="n">info</span><span class="o">.</span><span class="n">current_w</span>
</span></span><span class="line"><span class="ln" id="hl-8-30"><a class="lnlinks" href="#hl-8-30">30</a></span><span class="cl"><span class="n">sh</span> <span class="o">=</span> <span class="n">info</span><span class="o">.</span><span class="n">current_h</span>
</span></span><span class="line"><span class="ln" id="hl-8-31"><a class="lnlinks" href="#hl-8-31">31</a></span><span class="cl"><span class="n">y</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln" id="hl-8-32"><a class="lnlinks" href="#hl-8-32">32</a></span><span class="cl"><span class="n">phrase</span> <span class="o">=</span> <span class="s2">&#34;Hi there. How are you doing&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-8-33"><a class="lnlinks" href="#hl-8-33">33</a></span><span class="cl"><span class="n">windowSurface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">WHITE</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-34"><a class="lnlinks" href="#hl-8-34">34</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">YELLOW</span> <span class="p">,</span> <span class="p">(</span><span class="mi">250</span><span class="p">,</span><span class="mi">200</span><span class="p">),</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-35"><a class="lnlinks" href="#hl-8-35">35</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">280</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-36"><a class="lnlinks" href="#hl-8-36">36</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">220</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-37"><a class="lnlinks" href="#hl-8-37">37</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-38"><a class="lnlinks" href="#hl-8-38">38</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">ellipse</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span><span class="n">BLACK</span><span class="p">,(</span><span class="mi">225</span><span class="p">,</span><span class="mi">230</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span><span class="mi">10</span><span class="p">),</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-39"><a class="lnlinks" href="#hl-8-39">39</a></span><span class="cl"><span class="n">myfont</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s2">&#34;ComicSans&#34;</span><span class="p">,</span> <span class="mi">35</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-40"><a class="lnlinks" href="#hl-8-40">40</a></span><span class="cl"><span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">update</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-41"><a class="lnlinks" href="#hl-8-41">41</a></span><span class="cl"><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-42"><a class="lnlinks" href="#hl-8-42">42</a></span><span class="cl"><span class="n">paragraph</span> <span class="o">=</span>  <span class="nb">str</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
</span></span><span class="line"><span class="ln" id="hl-8-43"><a class="lnlinks" href="#hl-8-43">43</a></span><span class="cl"><span class="n">thread</span><span class="o">.</span><span class="n">start_new_thread</span><span class="p">(</span> <span class="n">saySomething</span><span class="p">,(</span><span class="n">paragraph</span><span class="p">,</span><span class="s2">&#34;en&#34;</span><span class="p">,))</span>
</span></span><span class="line"><span class="ln" id="hl-8-44"><a class="lnlinks" href="#hl-8-44">44</a></span><span class="cl"><span class="n">workingSentence</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="ln" id="hl-8-45"><a class="lnlinks" href="#hl-8-45">45</a></span><span class="cl"><span class="n">sleep</span><span class="p">(</span><span class="mf">0.26</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-46"><a class="lnlinks" href="#hl-8-46">46</a></span><span class="cl"><span class="k">for</span> <span class="n">phrase</span> <span class="ow">in</span> <span class="n">paragraph</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;?&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-8-47"><a class="lnlinks" href="#hl-8-47">47</a></span><span class="cl">    <span class="k">for</span> <span class="n">sentence</span> <span class="ow">in</span> <span class="n">phrase</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="ln" id="hl-8-48"><a class="lnlinks" href="#hl-8-48">48</a></span><span class="cl">        <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">sentence</span><span class="o">.</span><span class="n">split</span><span class="p">():</span>
</span></span><span class="line"><span class="ln" id="hl-8-49"><a class="lnlinks" href="#hl-8-49">49</a></span><span class="cl">            <span class="n">windowSurface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">WHITE</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-50"><a class="lnlinks" href="#hl-8-50">50</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">YELLOW</span> <span class="p">,</span> <span class="p">(</span><span class="mi">250</span><span class="p">,</span><span class="mi">200</span><span class="p">),</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-51"><a class="lnlinks" href="#hl-8-51">51</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">280</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-52"><a class="lnlinks" href="#hl-8-52">52</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">220</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-53"><a class="lnlinks" href="#hl-8-53">53</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-54"><a class="lnlinks" href="#hl-8-54">54</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">ellipse</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span><span class="n">BLACK</span><span class="p">,(</span><span class="mi">225</span><span class="p">,</span><span class="mi">220</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span><span class="mi">30</span><span class="p">),</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-55"><a class="lnlinks" href="#hl-8-55">55</a></span><span class="cl">            <span class="n">myfont</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s2">&#34;ComicSans&#34;</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-56"><a class="lnlinks" href="#hl-8-56">56</a></span><span class="cl">            <span class="n">workingSentence</span> <span class="o">+=</span> <span class="n">word</span> <span class="o">+</span> <span class="s2">&#34; &#34;</span>
</span></span><span class="line"><span class="ln" id="hl-8-57"><a class="lnlinks" href="#hl-8-57">57</a></span><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="n">myfont</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">workingSentence</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-58"><a class="lnlinks" href="#hl-8-58">58</a></span><span class="cl">            <span class="n">windowSurface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-8-59"><a class="lnlinks" href="#hl-8-59">59</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">update</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-60"><a class="lnlinks" href="#hl-8-60">60</a></span><span class="cl">            <span class="n">syl</span> <span class="o">=</span> <span class="n">nsyl</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-61"><a class="lnlinks" href="#hl-8-61">61</a></span><span class="cl">            <span class="n">syl</span> <span class="o">=</span> <span class="n">syl</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="ln" id="hl-8-62"><a class="lnlinks" href="#hl-8-62">62</a></span><span class="cl">            <span class="n">sleep</span><span class="p">(</span><span class="mf">0.185</span><span class="o">*</span><span class="nb">float</span><span class="p">(</span><span class="n">syl</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-8-63"><a class="lnlinks" href="#hl-8-63">63</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-64"><a class="lnlinks" href="#hl-8-64">64</a></span><span class="cl">            <span class="n">windowSurface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">WHITE</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-65"><a class="lnlinks" href="#hl-8-65">65</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">YELLOW</span> <span class="p">,</span> <span class="p">(</span><span class="mi">250</span><span class="p">,</span><span class="mi">200</span><span class="p">),</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-66"><a class="lnlinks" href="#hl-8-66">66</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">280</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-67"><a class="lnlinks" href="#hl-8-67">67</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">220</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-68"><a class="lnlinks" href="#hl-8-68">68</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-69"><a class="lnlinks" href="#hl-8-69">69</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">ellipse</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span><span class="n">BLACK</span><span class="p">,(</span><span class="mi">225</span><span class="p">,</span><span class="mi">230</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span><span class="mi">10</span><span class="p">),</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-70"><a class="lnlinks" href="#hl-8-70">70</a></span><span class="cl">            <span class="n">myfont</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s2">&#34;ComicSans&#34;</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-71"><a class="lnlinks" href="#hl-8-71">71</a></span><span class="cl">            <span class="n">label</span> <span class="o">=</span> <span class="n">myfont</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">workingSentence</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-72"><a class="lnlinks" href="#hl-8-72">72</a></span><span class="cl">            <span class="n">windowSurface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-8-73"><a class="lnlinks" href="#hl-8-73">73</a></span><span class="cl">            <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">update</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-74"><a class="lnlinks" href="#hl-8-74">74</a></span><span class="cl">            <span class="n">sleep</span><span class="p">(</span><span class="mf">0.082</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-75"><a class="lnlinks" href="#hl-8-75">75</a></span><span class="cl">        <span class="n">windowSurface</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">WHITE</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-76"><a class="lnlinks" href="#hl-8-76">76</a></span><span class="cl">        <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">YELLOW</span> <span class="p">,</span> <span class="p">(</span><span class="mi">250</span><span class="p">,</span><span class="mi">200</span><span class="p">),</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-77"><a class="lnlinks" href="#hl-8-77">77</a></span><span class="cl">        <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">280</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-78"><a class="lnlinks" href="#hl-8-78">78</a></span><span class="cl">        <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">circle</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">,(</span><span class="mi">220</span><span class="p">,</span><span class="mi">170</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-79"><a class="lnlinks" href="#hl-8-79">79</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-80"><a class="lnlinks" href="#hl-8-80">80</a></span><span class="cl">        <span class="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">ellipse</span><span class="p">(</span><span class="n">windowSurface</span><span class="p">,</span><span class="n">BLACK</span><span class="p">,(</span><span class="mi">225</span><span class="p">,</span><span class="mi">230</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span><span class="mi">10</span><span class="p">),</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-81"><a class="lnlinks" href="#hl-8-81">81</a></span><span class="cl">        <span class="n">myfont</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s2">&#34;ComicSans&#34;</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-82"><a class="lnlinks" href="#hl-8-82">82</a></span><span class="cl">        <span class="n">label</span> <span class="o">=</span> <span class="n">myfont</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">workingSentence</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">BLACK</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-83"><a class="lnlinks" href="#hl-8-83">83</a></span><span class="cl">        <span class="n">windowSurface</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="ln" id="hl-8-84"><a class="lnlinks" href="#hl-8-84">84</a></span><span class="cl">        <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">update</span><span class="p">()</span>
</span></span><span class="line"><span class="ln" id="hl-8-85"><a class="lnlinks" href="#hl-8-85">85</a></span><span class="cl">        <span class="n">sleep</span><span class="p">(</span><span class="mf">0.16</span><span class="p">)</span>
</span></span><span class="line"><span class="ln" id="hl-8-86"><a class="lnlinks" href="#hl-8-86">86</a></span><span class="cl">
</span></span><span class="line"><span class="ln" id="hl-8-87"><a class="lnlinks" href="#hl-8-87">87</a></span><span class="cl"><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="adding-personality">Adding personality</h2>
<p>This part is actually very easy - <a href="https://code.google.com/p/chatter-bot-api/" target="_blank" >there is a fantastic API already available that does the job</a>. Use this API and then all that needs to be done for the AI program is creating a function that feeds in the query to the robot and gets the response:</p>
<pre tabindex="0"><code>from chatterbotapi import ChatterBotFactory, ChatterBotType

def getAIresponse2(s):
    factory = ChatterBotFactory()
    bot2 = factory.create(ChatterBotType.CLEVERBOT)
    bot2session = bot2.create_session()
    response = bot2session.think(s)
    return response
</code></pre><p>In this example I use <code>CLEVERBOT</code> but there are certainly other bots to choose from. A little later I will talk about the predicament to develop a personalized chatterbot baesd on chat histories.</p>
]]></description>
    </item>
    
    <item>
	    <title>Books I read (2008)</title>
	    <image>https://schollz.com</image>
      <link>/tinker/books-2008/</link>
      <pubDate>Wed, 31 Dec 2008 15:00:45 -0700</pubDate>
      
      <guid>/tinker/books-2008/</guid>
      <description><![CDATA[<p>Here is a comprehensive list of all the books that I read in 2008. This list does not include Sherman&rsquo;s Lagoon or Get Fuzzy comic books, and it does not include textbooks.</p>
<ol>
<li>"Jonathan Strange" by Susanna Clarke: This is a great magical realism book. It is along the lines of Harry Potter, and it depicts the magical epoch in the 19th century England. The two main characters share opposing extremist philosophies on the purpose knowledge: knowledge for the sake of knowing, or knowledge for application.</li>
<li>"Like Water for Chocolate" by Laura Esquivel: This is another magical realism book, but based more around the magical culinary properties. The characters are very interesting, and their are pretty good recipes sprinkled in the book.
</li>
<li>"100 Years of Solitude" by García Márquez: Amazing book. The all-prevailing persistent mother is very inspiring.</li>
<li>Many Terry Prachett novels including The Colour of Magic and #5-32</li>
<li>The Light Fantastic</li>
<li>Equal Rites</li>
<li>Mort</li>
<li>Sourcery</li>
<li>Wyrd Sisters</li>
<li>Pyramids</li>
<li>Guards! Guards!</li>
<li>Eric</li>
<li>Moving Pictures</li>
<li>Reaper Man</li>
<li>Witches Abroad</li>
<li>Small Gods</li>
<li>Lords and Ladies</li>
<li>Men at Arms</li>
<li>Soul Music</li>
<li>Interesting Times</li>
<li>Maskerade</li>
<li>Feet of Clay</li>
<li>Hogfather</li>
<li>Jingo</li>
<li>The Last Continent</li>
<li>Carpe Jugulum</li>
<li>The Fifth Elephant</li>
<li>The Truth</li>
<li>Thief of Time</li>
<li>The Last Hero</li>
<li>Monstrous Regiment</li>
<li>Going Postal</li>
<li>"Dear Theo" by Vincent Van Gough: Letters to Van Gough's art dealing brother, Theo. These letters are riddled with inspirational dedication to art and life.</li>
<li>"The Collected Poems of Robert Creeley 1945-1975": I found about Robert Creeley through references from Robert Hunter after reading lyrics in the "Complete Annotated Grateful Dead Lyrics." Robert Creeley is a extremely talented poet, and a lot of his poetry reminds me of the beatnik style of poetry that emerged in the 50's.</li>
<li>"Zen and the Art of Motorcycle Maintenance" by Robert Pirsig: This is a great book that centrals about the notion of quality.</li>
<li>"Innumerancy" by John Paulos: Numerous examples of how the general society has trouble with basic numbers. This book is another reason why words for numbers like "billion" and "trillion" should be eliminated and replaced by scientific notation.</li>
<li>"The Selfish Gene" by Richard Dawkins: A great book about how genotype becomes phenotype and why it explains many behaviors of many animals. Written by the most vehement atheist in the world.</li>
<li>"The Elegant Universe" by Brain Greene: If your interested in String theory, just read the first chapter. The book doesn't delve much farther into one of the leading Unified Theories of Everything than the first chapter and the rest of the book is pretty boring.</li>
<li>"This is Your Brain on Music" by David Levitin: All about music from a music producer turned neurologist. The definition of music is more precise than many other scientific books I've read. The second half of the book is mostly devoted to how music affects the brain neurologically which is fascinating to no end.</li>
<li>"Egg and Ego" by J.M. Slack: Life in a laboratory with great anecdotes.]]></description>
    </item>
    
    <item>
	    <title>Ten commandments for a scientist.</title>
	    <image>https://schollz.com/img/measurements.jpg</image>
      <link>/tinker/ten-commandments/</link>
      <pubDate>Tue, 03 Jun 2008 15:19:41 -0700</pubDate>
      
      <guid>/tinker/ten-commandments/</guid>
      <description><![CDATA[<p>Moses reached upon Mt. Sinai and was given the gift of the Ten Commandments which have dictated the fundamental rules of life ever since. Scrawled on the back of the Ten Commandments was actually another set of commandments that determined the virtuousness of science and the held commandments to follow when Moses led his people to the promised land to live fruitfully in the pursuit of scientific knowledge.</p>
<h2 id="i-thou-shalt-always-vortex-thy-cells">I. Thou shalt always vortex thy cells.</h2>
<p>Often times suspensions contain elements of variable densities. Even after short periods of time, gravity causes denser particles to migrate toward the bottom of the container. This principle imbues that the distribution of a medium should be completely uniform before pipetting and measuring. The consequences of ignorance of the first commandment could result in large systematic errors.</p>
<h2 id="ii-thou-shalt-never-fabricate-experimental-evidence">II. Thou shalt never fabricate experimental evidence.</h2>
<p>Science is the search for knowledge, and knowledge is the truth of the universe. By fabricating information about a research project, the scientist is violating the foundation of science itself. The consequences of this commandment will result in the revocation of the scientist&rsquo;s reputability and status.</p>
<h2 id="iii-thou-shalt-sanitize-thy-work-bench">III. Thou shalt sanitize thy work bench</h2>
<p>This commandment ensures that no experiment will be conducted with error of contamination. The lab bench should always be cleansed with ethanol and Bunson burners should be used to sanitize the tops of bottles when they are opened. Keeping organized may will be inherent in this commandment as well. Searching for something delays much time that could be spent working on hypothesis or experiments. Cleanliness further enables a scientist to undergo optimal scientific activity.</p>
<h2 id="iv-thy-safety-goggles-shalt-be-worneth-when-thou-workst-with-chemicals">IV. Thy safety goggles shalt be worneth when thou workst with chemicals</h2>
<p>Even if the safety goggles are not needed, because of blindness or lack of eyeballs, the safety goggles should be worn. This commandment is not just meant to keep your eyes safe form the harmful chemicals. This commandment was put into place to insure that everything you look at will be seen through a pair of safety glasses. The safety glasses are to insure that your intuition is to automatically create a safe and productive research atmosphere. The glass in which you view the world should be a glass reveals safety.
V. Labels shalt be adhered to containers</p>
<p>Countless test tubes and containers are used every day containing a variety of different colonies or substances. To keep experiments from contamination the correct labels should clearly dictate what any container holds. Writing of the date will imply the correct calculations when performing a time variant experiment.</p>
<h2 id="vi-thou-shalt-keepeth-a-lab-notebook">VI. Thou shalt keepeth a lab notebook</h2>
<p>No memory is infallible. In order to maximize progress, a lab notebook should be established to inform one&rsquo;s self and others on the progress of a research campaign. The lack of a notebook can cause confusion or failure of a experiment.</p>
<h2 id="vii-thou-shalt-doubteth-everything">VII. Thou shalt doubteth everything.</h2>
<p>To a certain extent, a good researcher should doubt everything, with emphasis on presently accepted ideas. Throughout the 17th and 18th century, science had to feud with the rule of the church. Eventually the church doctrine backed down and allowed science to advance. However, today we are still restrained by our absolute faith in the past.</p>
<p><img src="/img/measurements.jpg" alt="Elementary particle measurements"></p>
<p>This image shows measurements made on types of particles and the error from past publications. Notice that the measurement and error stays constant and suddenly changes abruptly. This abrupt change is because the experiment did not attempt to conform to previous methods.</p>
<p>Experiments can be carried out endlessly. Tweaking the experiment over and over to get the same result as a previous publication will only dampen the progress of science. Consider the previous publications heavily, but doubt constantly.</p>
<h2 id="viii-thou-shalt-not-talk-fast">VIII. Thou shalt not talk fast.</h2>
<p>Think before talking. Make sure you understand the concept in your head clearly so it can be verbalized to someone else. The teacher benefits from a deeper understanding of the concept and the student benefits from having the idea explained concisely and slowly. Use measured words when talking. Don&rsquo;t let your mouth become plagued by garrulousness.</p>
<h2 id="ix-haste-makes-waste">IX. Haste makes waste.</h2>
<p>Scribbling in a lab notebook or scrawled labels will make life difficult later. Sloppy experiments create irreproducible results, and makes a waste of time for the whole lab. It is possible to be timely and organized. A mistake which is an accident can result in a new discovery. A mistake which is caused by rushing to put out results will almost always result in a waste of materials and time. Patience is the key virtue for this commandment.</p>
<h2 id="x-thou-shalt-enjoy-life">X. Thou shalt enjoy life</h2>
<p>This commandment is possibly the hardest to maintain. The tenth commandment deems having a healthy lifestyle, which equates to eating all the basic food groups in their respective weights, regular exercise, and reading the comics in the newspaper every day. Keep everything in moderation.</p>
]]></description>
    </item>
    
    <item>
	    <title>Scientific perspective on genesis.</title>
	    <image>https://schollz.com/img/bigbang.jpg</image>
      <link>/tinker/genesis/</link>
      <pubDate>Mon, 02 Jun 2008 21:54:46 -0700</pubDate>
      
      <guid>/tinker/genesis/</guid>
      <description><![CDATA[<p>In the beginning, 10 to 20 times 10<sup>9</sup> years ago, there was a big bang. A hundredth of a second later electrons, photons and neutrinos appeared. Three minutes later the quarks of the fundamental particles formed into light nuclei, approximently 76% Hydrogen and 24% Helium. 300,000 years later G-d said, &ldquo;Let there be light&rdquo;; and the nuclei combined with electrons to form neutral atoms, making the universe transparent to photons.</p>
<p>G-d saw that the light was good, and created and destroyed stars 10<sup>9</sup> years later. The recycling of the cosmos produced and dispersed elements into the universe. 3.6x10<sup>9</sup> years after the first star formed, the heavier elements combined with Hydrogen and Helium to form our Sun and Solar System. He separated the light from the darkness. G-d called the light &ldquo;day,&rdquo; and the darkness he called &ldquo;night.&rdquo; And there was evening, and there was morning—the first day.</p>
<p>About 4x10<sup>8</sup> years after the formation of the Solar system G-d said, &ldquo;Let there be an expanse between the waters to separate water from water.&rdquo; So G-d made the expanse and separated the water under the expanse from the water above it. And it was so. G-d called the expanse &ldquo;sky.&rdquo; And G-d said, &ldquo;Let the water under the sky be gathered to one place, and let dry ground appear.&rdquo; And it was so. G-d called the dry ground &ldquo;land,&rdquo; and the gathered waters he called &ldquo;seas.&rdquo; And G-d saw that it was good. And there was evening, and there was morning—the second day.</p>
<p>Then, 5x10<sup>8</sup> years later G-d said, &ldquo;Let the land produce self-replicating organisms: each with DNA-RNA that allowed for the formation of proteins to carry out certain functions.&rdquo; And it was so that prokaryotic cells formed. The land produced vegetation: plants bearing seed according to their kinds and trees bearing fruit with seed in it according to their kinds. And G-d saw that it was good. And there was evening, and there was morning—the third day.</p>
<p>On the fourth day, G-d rests.</p>
<p>And then 1.5x10<sup>9</sup> years later G-d said, &ldquo;Let the water teem with living creatures made eukaryotic cells.&rdquo; So G-d created the great creatures of the sea and every living and moving thing with which the water teems, according to their kinds. And G-d saw that it was good. G-d blessed them and said, &ldquo;Be fruitful and self-replicate and fill the water in the seas.&rdquo; And there was evening, and there was morning—the fifth day.</p>
<p>And then 7x10<sup>8</sup> years later G-d said, &ldquo;Let the land produce living creatures according to their kinds: livestock, creatures that move along the ground, and wild animals, each according to its kind.&rdquo; And it was called the Cambrian explosion. G-d made the wild animals according to their kinds, the livestock according to their kinds, and all the creatures that move along the ground according to their kinds. And G-d saw that it was good.</p>
<p>Finally, 6x10<sup>6</sup> years ago, G-d said, &ldquo;Let us make man/woman evolve from currently animals.&rdquo; So man/woman diverged from a chimpanzee. G-d blessed them and said to them, &ldquo;Be fruitful and increase in number; fill the earth and subdue it. Rule over the fish of the sea and the birds of the air and over every living creature that moves on the ground.&rdquo;</p>
<p>Then G-d said, &ldquo;I give you every seed-bearing plant on the face of the whole earth and every tree that has fruit with seed in it. They will be yours for food. And to all the beasts of the earth and all the birds of the air and all the creatures that move on the ground—everything that has the breath of life in it—I give every green plant for food.&rdquo; And it was so.</p>
<p>G-d saw all that he had made, and it was very good. And there was evening, and there was morning—the sixth day.</p>
<p>G-d has not finished the heavens and the earth in all their vast array. He is resting until the seventh day, 4.94x10<sup>8</sup> years from now.</p>
]]></description>
    </item>
    
    <item>
	    <title></title>
	    <image>https://schollz.com</image>
      <link>/tinker/devine-lu-linvega/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>/tinker/devine-lu-linvega/</guid>
      <description><![CDATA[<blockquote>
<p>&ldquo;&hellip;complexity in software is explained in the lowest number of rules, instructions to maintain a given state&hellip;&rdquo; - adapted from Komolgorov</p>
</blockquote>
<blockquote>
<p>&ldquo;elegance is defined in terms of limits. elegance is articulating the value of absence.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;sabotage is the withdrawal of efficiency&rdquo;</p>
</blockquote>
]]></description>
    </item>
    
  </channel>
</rss>
