<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Flavio De Stefano</title>
    <description>The latest articles on DEV Community by Flavio De Stefano (@kopiro).</description>
    <link>https://dev.to/kopiro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F386888%2Ff683c0ad-bdf7-4401-9141-46416239d66d.jpg</url>
      <title>DEV Community: Flavio De Stefano</title>
      <link>https://dev.to/kopiro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kopiro"/>
    <language>en</language>
    <item>
      <title>Convert a batch of images into Ghibli style using ChatGPT and macOS Automator</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Sun, 13 Apr 2025 12:51:05 +0000</pubDate>
      <link>https://dev.to/kopiro/process-bunch-of-pictures-into-ghibli-style-using-chatgpt-and-macos-automator-4a37</link>
      <guid>https://dev.to/kopiro/process-bunch-of-pictures-into-ghibli-style-using-chatgpt-and-macos-automator-4a37</guid>
      <description>&lt;p&gt;If you love the warm, dreamy visuals of Studio Ghibli films, here's a powerful automation trick: an AppleScript automation that lets you select multiple images from Finder, apply a Ghibli-style transformation using ChatGPT, and process them unattended — even overnight.&lt;/p&gt;

&lt;p&gt;You can also use this script while working/using your Mac: the script will warn you when not to touch the keyboard or the mouse because it's working for you!&lt;/p&gt;

&lt;p&gt;This setup uses native macOS tools like AppleScript, Automator, and Notification Center, and it respects API rate limits to avoid disruptions.&lt;/p&gt;

&lt;h4&gt;
  
  
  AppleScript &amp;amp; Automator
&lt;/h4&gt;

&lt;p&gt;AppleScript is Apple’s native scripting language for automating actions across macOS apps. It can simulate user actions, trigger shell commands, and communicate with apps like Finder, Preview, and Automator. Automator is an application that can be used to run AppleScript.&lt;/p&gt;

&lt;h4&gt;
  
  
  About Rate Limiting
&lt;/h4&gt;

&lt;p&gt;Rate limiting is a mechanism used by APIs and services like ChatGPT to prevent overuse or abuse. &lt;/p&gt;

&lt;p&gt;It controls how many requests you can send in a given time frame. If you exceed these limits, ChatGPT may suspend your user or not process the request correctly.&lt;/p&gt;

&lt;p&gt;To avoid this, the script includes a fixed delay of 180s between each image. This pause ensures each image is processed without hitting the rate limits.&lt;/p&gt;

&lt;p&gt;⚠️ Do not reduce or remove this delay. If you try to speed things up, the script may break mid-process or get you temporarily blocked from uploading.&lt;/p&gt;

&lt;h3&gt;
  
  
  The script and the workflow
&lt;/h3&gt;

&lt;p&gt;First of all, you need to install the web-app of ChatGPT from Chrome. When I tried to do this with the native ChatGPT macOS app, it wouldn't generate restyle of images correctly.&lt;/p&gt;

&lt;p&gt;Therefore, just head to &lt;a href="https://chat.com" rel="noopener noreferrer"&gt;chat.com&lt;/a&gt;, and in the Chrome menu hit "Install Page as App"&lt;/p&gt;

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

&lt;p&gt;Now, let's  install the workflow into Automator:&lt;/p&gt;

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

&lt;p&gt;You can download the workflow directly from &lt;a href="https://kopiro.s3.amazonaws.com/var/FilesToChatGPT.workflow.zip" rel="noopener noreferrer"&gt;this link&lt;/a&gt; - extract it and Automator will open; then &lt;strong&gt;Press Run&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;And that's it - the script should go into each file, paste it into ChatGPT, generate the request and wait 3 minutes until next request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative installation
&lt;/h3&gt;

&lt;p&gt;If you don't trust downloading semi-executable files into your system, you can create a new workflow manually. &lt;/p&gt;

&lt;p&gt;Make sure as first step you add "Ask For Finder Items", then "Run AppleScript" and paste this code:&lt;/p&gt;

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


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Using another style
&lt;/h3&gt;

&lt;p&gt;The prompt you use is highly customizable. Just change that in the code to add some details or to make images into another completely different style.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>automator</category>
      <category>chatgpt</category>
      <category>ghibli</category>
    </item>
    <item>
      <title>How to correctly publish your Mac apps outside of the App Store</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Fri, 11 Apr 2025 12:48:47 +0000</pubDate>
      <link>https://dev.to/kopiro/how-to-correctly-publish-your-mac-apps-outside-of-the-app-store-38a</link>
      <guid>https://dev.to/kopiro/how-to-correctly-publish-your-mac-apps-outside-of-the-app-store-38a</guid>
      <description>&lt;p&gt;Distributing Mac apps outside of the App Store requires more than just building and zipping your .app. &lt;/p&gt;

&lt;p&gt;Since macOS Catalina, Apple requires all apps to be notarized by Apple to run without warnings on users' machines. Notarization ensures the app is from a known developer and free of malicious code.&lt;/p&gt;

&lt;p&gt;Without notarization, users will see a warning saying the app “can’t be opened because Apple cannot check it for malicious software.”&lt;/p&gt;

&lt;p&gt;With proper notarization and signing, your app behaves like a first-class citizen on macOS, even outside the App Store.&lt;/p&gt;

&lt;p&gt;Here’s a streamlined process to do it correctly:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Archive Your App in Xcode
&lt;/h3&gt;

&lt;p&gt;Open your project in Xcode and create an archive:&lt;/p&gt;

&lt;p&gt;In the menu, go to Product &amp;gt; Archive.&lt;/p&gt;

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

&lt;p&gt;After the archive builds, the Organizer window will open. Click "Distribute App" on the build you want to export, then click "Direct Distribution".&lt;/p&gt;

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

&lt;p&gt;When it's done, click "Export", and save the app somewhere; you'll receive a signed .app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create the notarized DMG
&lt;/h3&gt;

&lt;p&gt;To simplify post-export steps, I wrote a script that takes your exported .app, packages it into a .dmg, submits it for notarization, and staples the result — all automatically.&lt;/p&gt;

&lt;p&gt;Before doing anything, install the necessary tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;create-dmg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we want to export the credentials that you already have into Xcode to the keychain, so that other tools can use it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xcrun notarytool store-credentials "AC_PASSWORD" --apple-id $EMAIL --team-id $TEAM_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Perfect - you can now use &lt;a href="https://gist.github.com/kopiro/7b77b2a6d3dfc2c5359ff0f25667747b" rel="noopener noreferrer"&gt;my create-my-dmg.sh script&lt;/a&gt; to create a DMG.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;You can run it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;create-my-dmg.sh YourMacApp.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a DMG like &lt;code&gt;YourMacApp-1.2.0.dmg&lt;/code&gt;, correctly notarized and stapled.&lt;/p&gt;

</description>
      <category>appstore</category>
      <category>macos</category>
    </item>
    <item>
      <title>Using C89 on a '80s Macintosh SE to solve Advent of Code 2023</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Sun, 03 Dec 2023 13:50:30 +0000</pubDate>
      <link>https://dev.to/kopiro/using-c89-and-a-macintosh-se-to-solve-advent-of-code-2023-44b1</link>
      <guid>https://dev.to/kopiro/using-c89-and-a-macintosh-se-to-solve-advent-of-code-2023-44b1</guid>
      <description>&lt;p&gt;For this year &lt;a href="https://adventofcode.com/" rel="noopener noreferrer"&gt;AdventOfCode&lt;/a&gt;, I want to try to solve some problems using an old Macintosh SE I have lying around and the ThinkC compiler.&lt;/p&gt;

&lt;p&gt;My goal is to learn more about the old C89 syntax and also try to get as efficient as possible with memory and CPU so that even more complex problems can be solved in reasonable time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 0
&lt;/h3&gt;

&lt;p&gt;First of all, I had to resurrect the old Macintosh and make sure everything works correctly. With my surprise, the &lt;a href="https://beyondloom.com/blog/thinkc.html" rel="noopener noreferrer"&gt;ThinkC compiler&lt;/a&gt; was already installed from 3 years ago and I managed to run a very quick "Hello world".&lt;/p&gt;

&lt;p&gt;The IDE that comes with the ThinkC compiler, called ThinkC Project manager, it's surprisingly good for those years.&lt;/p&gt;

&lt;p&gt;Then, I had to make sure I could still use my 1.4MB floppy disks with a modern MacBook. The TLDR is that old Macintoshs use HFS file systems and the support for this has been removed, therefore you can't just mount the floppy and see the files in the Finder of your new macOS Sonoma.&lt;/p&gt;

&lt;p&gt;After playing a bit with macFUSE and &lt;em&gt;not&lt;/em&gt; get anything working, I managed to simply mount the disk and copy the files using &lt;code&gt;hfsutils&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install hfsutils

# Mount the floppy
sudo hmount /dev/disk6

# Copy the input data
sudo hcopy -a ~/Developer/aoc/2023/1/input.txt :1.txt

# Copy the source from the Macintosh
sudo hcopy -a :1.c ~/Developer/aoc/2023/1/main.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I strangely could't open the input.txt on the Macintosh itself, but the C program I wrote is able to read the file correctly using &lt;code&gt;fopen&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 1 learnings
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Declarations at the beginning of the scope
&lt;/h4&gt;

&lt;p&gt;ANSI C89 requires variables to be declared at the beginning of a scope; this is not valid (syntax error on line 3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  snprintf doesn't exist
&lt;/h4&gt;

&lt;p&gt;The safer version of &lt;a href="https://cplusplus.com/reference/cstdio/snprintf/" rel="noopener noreferrer"&gt;sprintf&lt;/a&gt; to do string concatenation doesn't exists; no big deal, I don't really care about safety in this scenario.&lt;/p&gt;

&lt;h4&gt;
  
  
  I hit 32k limit with &lt;code&gt;int sum&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;My &lt;code&gt;int sum&lt;/code&gt; went negative as soon I hit the number 32k. &lt;/p&gt;

&lt;p&gt;I thought that this CPU was 32bit, but something it telling me that it's rather 16bit.&lt;/p&gt;

&lt;p&gt;Switching to &lt;code&gt;unsigned long&lt;/code&gt; and also making sure I use &lt;code&gt;printf("%lu", sum)&lt;/code&gt; (took 20m to realize I was still using &lt;code&gt;%d&lt;/code&gt;) made it work.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hey Siri, make coffee!</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Thu, 21 Jan 2021 11:33:22 +0000</pubDate>
      <link>https://dev.to/kopiro/hey-siri-make-coffee-2n9p</link>
      <guid>https://dev.to/kopiro/hey-siri-make-coffee-2n9p</guid>
      <description>&lt;p&gt;&lt;em&gt;“my wife was tired of bringing me coffee in bed every morning. But I am too lazy (especially in the morning) to actually get up and make it. Therefore, I automated it.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My goal was exactly the one described in the first sentence, I needed to automate the first coffee I get in the morning, ‘cause I can’t really do anything if I don’t get otherwise. Since I have a cheap Nespresso machine, it can’t be programmed to do so; therefore I decided to solve it using simple programming and iOS automation.&lt;/p&gt;

&lt;p&gt;This is definitely not sci-fi, not even complex programming or electronic. But it works, and the solution is quite simple and brilliant in my opinion, so I thought I could share it so others may benefit from it.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GYO14ioZ4Rw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  What you need:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ESP8266 board (I use the NodeMCU; it's very cheap (5$) and it does the job)&lt;/li&gt;
&lt;li&gt;A cheap servo motor (I use cheaps &lt;a href="https://github.com/kopiro/arduino/blob/main/stuff/hextronik-hxt900.jpg" rel="noopener noreferrer"&gt;HXT900&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In theory, this is all you need; you could just write a simple sketch that will turn the motor at 8 AM and press the button.&lt;/p&gt;

&lt;p&gt;I soon realized though that to make the most of it, your coffee shouldn’t be made every day (there could be days when you aren’t at home) and it shouldn’t be made every day at the same time (I don’t know about you, but I don’t wake up every day at the same time, and also 5m could make the difference between a hot coffee vs a cold one).&lt;/p&gt;

&lt;p&gt;In my case, I every day end up drinking the coffee as soon I stop my alarm; that’s the moment when the button should be pressed; in order to do that, we can take advantage of some of the recent automation that Apple put in the latest iOS. I’m quite sure there is a similar concept in Android, but since I own an iPhone I’ll describe the procedure for this one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc56ulsht6ffzo7fgaxmv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc56ulsht6ffzo7fgaxmv.jpeg" alt="Alt Text" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect the servo
&lt;/h3&gt;

&lt;p&gt;That's pretty straightforward; your servo motor should have 3 pins: 3V, GROUND, and the controller pin.&lt;/p&gt;

&lt;p&gt;Connect these to your board, choose the controller pin and write it down; if you have a NodeMCU and the HXT900, you can use this schema:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ap1cn6qk9yth37xvmvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ap1cn6qk9yth37xvmvr.png" alt="Alt Text" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Download the sketch and flash it
&lt;/h3&gt;

&lt;p&gt;Download this sketch: &lt;a href="https://github.com/kopiro/arduino/blob/main/Projects/ButtonPresser/ButtonPresser.ino" rel="noopener noreferrer"&gt;https://github.com/kopiro/arduino/blob/main/Projects/ButtonPresser/ButtonPresser.ino&lt;/a&gt; to your workstation and open it.&lt;/p&gt;

&lt;p&gt;I'm not gonna cover here how to configure your workstation on how to build Arduino sketches or how to install ESP8266 drivers, as you can find all resources &lt;a href="https://www.espressif.com/en/products/socs/esp8266" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before flashing it, you should modify the sketch and set &lt;code&gt;WIFI_ENABLED&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;WIFI_SSID&lt;/code&gt; and &lt;code&gt;WIFI_PASS&lt;/code&gt; to reflect your network configuration, so that the board is able to connect to it.&lt;/p&gt;

&lt;p&gt;Also, make sure you set the &lt;code&gt;MDNS_ENABLED&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;MDNS_NAME&lt;/code&gt; to something that makes sense for you; this is how you can reach your board in your network without dealing with static DHCP IP allocation on your router.&lt;/p&gt;

&lt;p&gt;Make sure you also change the &lt;code&gt;SERVO_GPIO&lt;/code&gt; to be the GPIO number where you have connected your servo controller pin.&lt;/p&gt;

&lt;p&gt;If you want to make sure you can update the board via OTA (over-the-air; without connecting the board via USB to the workstation), please also set &lt;code&gt;OTA_ENABLED&lt;/code&gt; to 1 and set the &lt;code&gt;OTA_PASS&lt;/code&gt; to something unique.&lt;/p&gt;

&lt;p&gt;Once you have flashed your board, it should be accessible via the &lt;code&gt;MDNS_NAME&lt;/code&gt; you choose in your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdxd69zq3o8av7dpe53tk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdxd69zq3o8av7dpe53tk.png" alt="Alt Text" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a simple dashboard where you can play with the software to choose the right values based on your coffee machine, on how long, how far, and how many times the button should be pressed.&lt;/p&gt;

&lt;p&gt;Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;degree&lt;/code&gt; is a numeric value representing how many degrees the servo should turn. If you have put the button at 0 degree, something between 20 and 45 should do the job to press a button.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delay&lt;/code&gt; is the number of milliseconds the board should wait before lifting the servo back and turning it again to &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;times&lt;/code&gt; is how many times this “push” button operation should be repeated. This is quite useful in my case because I have to press once to switch on the coffee machine and twice to actually pour the coffee.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delaytimes&lt;/code&gt; is the number of milliseconds to wait between every “push” button operation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Play with these values until you are sure that the coffee machine is able to press the button properly, and store them for later.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the sketch is really doing
&lt;/h3&gt;

&lt;p&gt;Nothing fancy, actually.&lt;/p&gt;

&lt;p&gt;It's simple exposing an HTTP server with a POST method called &lt;code&gt;/api&lt;/code&gt; that accepts those parameters you played with before; when you hit this endpoint, it will simply turn the servo by the &lt;code&gt;degree&lt;/code&gt; parameter specified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_degree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"degree"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"delay"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"times"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_delaytimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"delaytimes"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"push"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_degree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_degree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_times&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_delaytimes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_delaytimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;_times&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;myservo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_degree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_delay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;myservo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;_times&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_delaytimes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/plain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you make a request using cURL to that endpoint, you can test from your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://buttonpresser.local/api &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"method=push&amp;amp;degree=20&amp;amp;times=2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Make coffee every morning after you have stopped your alarm
&lt;/h3&gt;

&lt;p&gt;This is the simplest and funniest part; we’re gonna just take advantage of the &lt;em&gt;“Shortcut”&lt;/em&gt; app already available on your iOS device.&lt;/p&gt;

&lt;p&gt;Create a new shortcut with the action of &lt;em&gt;”Get Contents of URL”&lt;/em&gt; and put these values:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F27oyltgsmftqkvi1iqez.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F27oyltgsmftqkvi1iqez.jpeg" alt="Alt Text" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now test this shortcut by saying to Siri, or just by pressing the button.&lt;/p&gt;

&lt;p&gt;How to do the automation though?&lt;/p&gt;

&lt;p&gt;That’s even simpler, go to the &lt;em&gt;Automation&lt;/em&gt; tab, and select &lt;em&gt;When my alarm has stopped&lt;/em&gt; &amp;gt; &lt;em&gt;Run shortcut&lt;/em&gt; and select the shortcut you’ve previously created!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq8c4h94wr802fmmr79ol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq8c4h94wr802fmmr79ol.png" alt="Alt Text" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7mj1xbxbm4kjlrm64ob1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7mj1xbxbm4kjlrm64ob1.png" alt="Alt Text" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F17aq3laeklk14pezigxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F17aq3laeklk14pezigxb.png" alt="Alt Text" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s all, for real... Happy morning coffee!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1350822814857777157-855" src="https://platform.twitter.com/embed/Tweet.html?id=1350822814857777157"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1350822814857777157-855');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1350822814857777157&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>ios</category>
      <category>automation</category>
      <category>arduino</category>
      <category>iot</category>
    </item>
    <item>
      <title>How I built the SiriWaveJS library: a look at the math and the code</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Mon, 03 Aug 2020 11:30:58 +0000</pubDate>
      <link>https://dev.to/kopiro/how-i-built-the-siriwavejs-library-a-look-at-the-math-and-the-code-l0o</link>
      <guid>https://dev.to/kopiro/how-i-built-the-siriwavejs-library-a-look-at-the-math-and-the-code-l0o</guid>
      <description>&lt;p&gt;It was 4 years ago when I had the idea to replicate the Apple® Siri wave-form (introduced with the iPhone 4S) in the browser using pure Javascript.&lt;/p&gt;

&lt;p&gt;During the last month, I updated this library by doing a lot of refactoring using ES6 features and reviewed the build process using &lt;strong&gt;RollupJS&lt;/strong&gt;. Now I’ve decided to share what I've learned during this process and the math behind this library.&lt;/p&gt;

&lt;p&gt;To get an idea of what the output will be, visit the &lt;a href="http://kopiro.github.io/siriwave/" rel="noopener noreferrer"&gt;live example&lt;/a&gt;; the whole codebase is &lt;a href="https://github.com/kopiro/siriwave" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, you can download all plots drawn in this article in GCX (OSX Grapher format): &lt;a href="https://github.com/kopiro/siriwave/raw/master/etc/gcx/default.gcx" rel="noopener noreferrer"&gt;default.gcx&lt;/a&gt; and &lt;a href="https://github.com/kopiro/siriwave/raw/master/etc/gcx/ios9.gcx" rel="noopener noreferrer"&gt;ios9.gcx&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The classic wave style
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkxe3c5bucq1ck68c4d6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkxe3c5bucq1ck68c4d6.gif" alt="Classic style" width="760" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially, this library only had the classic wave-form style that all of you remember using in iOS 7 and iOS 8.&lt;/p&gt;

&lt;p&gt;It’s no hard task to replicate this simple wave-form, only a bit of math and basic concepts of the Canvas API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvb2nrvtccivdvm2fl70.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvb2nrvtccivdvm2fl70.jpg" alt="Siri wave-form in iOS 7/8" width="640" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re probably thinking that the wave-form is a modification of the &lt;strong&gt;Sine&lt;/strong&gt; math equation, and you're right... well, almost right.&lt;/p&gt;

&lt;p&gt;Before starting to code, we’ve got to find our linear equation that will be simply applied afterward. My favorite plot editor is &lt;strong&gt;Grapher;&lt;/strong&gt; you can find it in any OSX installation under &lt;em&gt;Applications &amp;gt; Utilities &amp;gt; Grapher.app&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We start by drawing the well known &lt;code&gt;sin(x)&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcx007fo66tfebn0cfcsc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcx007fo66tfebn0cfcsc.png" alt="Plot for y = sin(x)" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfecto! Now, let’s add some parameters (Amplitude &lt;code&gt;[A]&lt;/code&gt;, Time coordinate &lt;code&gt;[t]&lt;/code&gt; and Spatial frequency &lt;code&gt;[k]&lt;/code&gt;) that will be useful later (Read more at: &lt;a href="https://en.wikipedia.org/wiki/Wave" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Wave&lt;/a&gt;).&lt;/p&gt;

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

&lt;p&gt;Now we have to “attenuate” this function on plot boundaries, so that for &lt;code&gt;|x| &amp;gt; 2&lt;/code&gt;, the &lt;code&gt;y&lt;/code&gt; values tends to 0. Let’s draw separately an equation &lt;code&gt;g(x)&lt;/code&gt; that has these characteristics.&lt;/p&gt;

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

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

&lt;p&gt;This seems to be a good equation to start with. Let’s add some parameters here too to smooth the curve for our purposes:&lt;/p&gt;

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

&lt;p&gt;Now, by multiplying our &lt;code&gt;f(x, …)&lt;/code&gt; and &lt;code&gt;g(x, …)&lt;/code&gt;, and by setting precise parameters to the other static values, we obtain something like this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;A = 0.9&lt;/code&gt; set the amplitude of the wave to max Y = A&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;k = 8&lt;/code&gt; set the spatial frequency and we obtain “more peaks” in the range [-2, 2]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;t = -π/2&lt;/code&gt; set the phase translation so that &lt;code&gt;f(0, …) = 1&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;K = 4&lt;/code&gt; set the factor for the “attenuation equation” so that the final equation is y = 0 when &lt;code&gt;|x| ≥ 2&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;It looks good! 😍&lt;/p&gt;

&lt;p&gt;Now, if you notice on the original wave we have other sub-waves that will give a lower value for the amplitude. Let’s draw them for &lt;code&gt;A = {0.8, 0.6, 0.4, 0.2, -0.2, -0.4, -0.6, -0.8}&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;In the final canvas composition, the sub-waves will be drawn with a decreasing opacity tending to &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic code concepts
&lt;/h3&gt;

&lt;p&gt;What do we do now with this equation?&lt;/p&gt;

&lt;p&gt;We use the equation to obtain the &lt;strong&gt;Y value&lt;/strong&gt; for an &lt;strong&gt;input X&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Basically, by using a simple &lt;strong&gt;for loop&lt;/strong&gt; from &lt;code&gt;-2 to 2&lt;/code&gt; (the &lt;em&gt;plot boundaries in this case&lt;/em&gt;), we have to draw &lt;strong&gt;point by point&lt;/strong&gt; the equation on the canvas using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath" rel="noopener noreferrer"&gt;beginPath&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo" rel="noopener noreferrer"&gt;lineTo&lt;/a&gt; API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_xpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_ypos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Probably this pseudo-code will clear up these ideas. We still have to implement our &lt;code&gt;_xpos&lt;/code&gt; and &lt;code&gt;_ypos&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;But… hey, what is &lt;code&gt;0.01&lt;/code&gt;???&lt;/p&gt;

&lt;p&gt;That value represents &lt;strong&gt;how many pixels&lt;/strong&gt; you move forward in each iteration before reaching the right plot boundary... but why 0.0.1, and what is the correct value?&lt;/p&gt;

&lt;p&gt;If you use a really small value (&lt;code&gt;&amp;lt; 0.01&lt;/code&gt;), you’ll get an insanely precise rendering of the graph but your performance will decrease because you’ll get too many iterations.&lt;/p&gt;

&lt;p&gt;Instead, if you use a really big value (&lt;code&gt;&amp;gt; 0.1&lt;/code&gt;) your graph will lose precision and you’ll notice this instantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2b95fqc0lqhv5orqgtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2b95fqc0lqhv5orqgtu.png" alt="Plot drawn with precision = 0.2" width="610" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that the final code is actually similar to the pseudo-code: &lt;a href="https://github.com/kopiro/siriwave/blob/master/src/curve.ts" rel="noopener noreferrer"&gt;https://github.com/kopiro/siriwave/blob/master/src/curve.ts&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement &lt;code&gt;_xpos(i)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You may argue that if we’re drawing the plot by incrementing the &lt;code&gt;x&lt;/code&gt;, then &lt;code&gt;_xpos&lt;/code&gt; may simply return the input argument.&lt;/p&gt;

&lt;p&gt;This is almost correct, but our plot is always drawn from &lt;code&gt;-B&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt; (&lt;code&gt;B = Boundary = 2&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;So, to draw on the canvas via &lt;strong&gt;pixel coordinates&lt;/strong&gt;, we must translate &lt;code&gt;-B to 0&lt;/code&gt; and &lt;code&gt;B to 1&lt;/code&gt; (simple transposition of [-B, B] to [0,1]); then multiply [0,1] and the &lt;strong&gt;canvas width (w)&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_xpos(i) = w * [ (i + B) / 2B ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implement &lt;code&gt;_ypos&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To implement &lt;code&gt;_ypos&lt;/code&gt;, we should simply write our equation obtained before (closely).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FREQ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_attFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_ypos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FREQ&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
        &lt;span class="nf"&gt;_attFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
        &lt;span class="nx"&gt;canvasHeight&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
        &lt;span class="nx"&gt;globalAmplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;attenuation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s clarify some parameters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;canvasHeight&lt;/code&gt; is Canvas height expressed in PX&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;i&lt;/code&gt; is our input value (the X)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;phase&lt;/code&gt; is the most important parameter, let’s discuss it later&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;globalAmplitude&lt;/code&gt; is a static parameter that represents the amplitude of the total wave (composed by sub-waves)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;attenuation&lt;/code&gt; is a static parameter that changes for each line and represents the amplitude of a wave&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase
&lt;/h3&gt;

&lt;p&gt;Now let’s discuss the &lt;strong&gt;phase variable&lt;/strong&gt;: it is the &lt;strong&gt;first changing variable&lt;/strong&gt; over time because it simulates the wave movement.&lt;/p&gt;

&lt;p&gt;What does it mean? It means that &lt;strong&gt;for each&lt;/strong&gt; animation frame, our base controller should &lt;strong&gt;increment&lt;/strong&gt; this value. But to avoid this value throwing a buffer overflow, let’s modulo it with 2π (since &lt;code&gt;Math.sin&lt;/code&gt; dominio is already modulo 2π).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We multiply &lt;code&gt;speed&lt;/code&gt; and &lt;code&gt;Math.PI&lt;/code&gt; so that with &lt;code&gt;speed = 1&lt;/code&gt;  we have the maximum speed (why? because &lt;code&gt;sin(0) = 0, sin(π/2) = 1, sin(π) = 0, ...&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finalizing
&lt;/h3&gt;

&lt;p&gt;Now that we have all code to draw a single line, we define a configuration array to draw all sub-waves and then cycle over them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attenuation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attenuation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attenuation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attenuation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// basic line&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attenuation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The iOS 9+ style
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8js0s0182j6gkkne8o6c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8js0s0182j6gkkne8o6c.gif" alt="GIF of SiriwaveJS iOS9+" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now things start to get complicated. The style introduced with iOS 9 is really complex and reverse engineering to simulate it’s not easy at all! I’m not fully satisfied with the final result, but I’ll continue to improve it until I get the desired result.&lt;/p&gt;

&lt;p&gt;As previously done, let’s start to obtain the linear equations of the waves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dhtm7uka5ya8yzedeuc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dhtm7uka5ya8yzedeuc.png" alt="Original Siri iOS 9+ wave-form" width="745" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We have three &lt;strong&gt;different specular equations&lt;/strong&gt; with different colors (&lt;strong&gt;green, blue, red&lt;/strong&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A single wave seems to be a &lt;strong&gt;sum of sine equations&lt;/strong&gt; with &lt;strong&gt;different parameters&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All other colors are a &lt;strong&gt;composition&lt;/strong&gt; of these three base colors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a **straight line **at the plot boundaries&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By picking again our previous equations, let’s define a more complex equation that &lt;strong&gt;involves translation&lt;/strong&gt;. We start by defining again our attenuation equation:&lt;/p&gt;

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

&lt;p&gt;Now, define &lt;code&gt;h(x, A, k, t)&lt;/code&gt; function, that is the sine function &lt;strong&gt;multiplied for **attenuation function&lt;/strong&gt;, in its absolute value:&lt;/p&gt;

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

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

&lt;p&gt;We now have a powerful tool.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;h(x)&lt;/code&gt;, we can now create the final wave-form by summing different &lt;strong&gt;h(x)&lt;/strong&gt; with different parameters involving different amplitudes, frequency, and translations. For example, let’s define the *&lt;em&gt;red curve *&lt;/em&gt; by putting random values.&lt;/p&gt;

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

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

&lt;p&gt;If we do the same with a &lt;strong&gt;green&lt;/strong&gt; and &lt;strong&gt;blue&lt;/strong&gt; curve, this is the result:&lt;/p&gt;

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

&lt;p&gt;This is not quite perfect, but it could work.&lt;/p&gt;

&lt;p&gt;To obtain the specular version, just multiply everything by &lt;code&gt;-1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the coding side, the approach is the same, we have only a more complex equation for &lt;code&gt;_ypos&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NO_OF_CURVES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This parameters should be generated randomly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;widths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amplitudes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_globalAttFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_ypos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ci&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;NO_OF_CURVES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offsets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;amplitudes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
            &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
            &lt;span class="nf"&gt;_globalAttFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;NO_OF_CURVES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;canvasHeightMax&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;globalAmplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s nothing complex here. The only thing that changed is that we cycle &lt;code&gt;NO_OF_CURVES&lt;/code&gt; times overall pseudo-random parameters and we &lt;strong&gt;sum&lt;/strong&gt; all &lt;strong&gt;y values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before multiplying it for &lt;code&gt;canvasHeightMax&lt;/code&gt; and  &lt;code&gt;globalAmplitude&lt;/code&gt; that give us the absolute PX coordinate of the canvas, we divide it for &lt;code&gt;NO_OF_CURVES&lt;/code&gt; so that &lt;strong&gt;y is always ≤ 1&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composite operation
&lt;/h3&gt;

&lt;p&gt;One thing that actually matters here is the &lt;code&gt;globalCompositeOperation&lt;/code&gt; mode to set in the Canvas. If you notice, in the original controller, when there’s an overlap of 2+ colors, they’re actually mixed in a standard way.&lt;/p&gt;

&lt;p&gt;The default is set to &lt;code&gt;source-over&lt;/code&gt;, but the result is poor, even with an opacity set.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk86vz3xjugof57nc8cml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk86vz3xjugof57nc8cml.png" alt="composite operation: source-over" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see all examples of vary **globalCompositeOperation **here: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By setting &lt;code&gt;globalCompositeOperation&lt;/code&gt; to &lt;code&gt;ligther&lt;/code&gt;, you notice that the intersection of the colors is nearest to the original.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Build with RollupJS
&lt;/h2&gt;

&lt;p&gt;Before refactoring everything, I wasn’t satisfied at all with the codebase: old prototype-like classes, a single Javascript file for everything, no uglify/minify and &lt;strong&gt;no build at all&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Using the new ES6 feature like &lt;strong&gt;native classes, spread operators&lt;/strong&gt; and &lt;strong&gt;lambda functions&lt;/strong&gt;, I was able to clean everything, split files, and decrease lines of unnecessary code.&lt;/p&gt;

&lt;p&gt;Furthermore, I used &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;RollupJS&lt;/a&gt; to create a transpiled and minified build in various formats.&lt;/p&gt;

&lt;p&gt;Since this is a browser-only library, I decided to create two builds: a &lt;strong&gt;UMD (Universal Module Definition)&lt;/strong&gt; build that you can use directly by importing the script or by using CDN, and another one as an &lt;strong&gt;ESM module&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The UMD module is built with this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/siriwave.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unpkg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amdName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;umd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;commonjs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;babel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An additional **minified UMD module **is built with this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/siriwave.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unpkg&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.min.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amdName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;umd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;commonjs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;babel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nf"&gt;uglify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefiting of UnPKG service, you can find the final build on this URL served by a CDN: &lt;a href="https://unpkg.com/siriwave/dist/siriwave.min.js" rel="noopener noreferrer"&gt;https://unpkg.com/siriwave/dist/siriwave.min.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the “old style Javascript way” — you can just import your script and then refer in your code by using &lt;code&gt;SiriWave&lt;/code&gt; global object.&lt;/p&gt;

&lt;p&gt;To provide a more elegant and modern way, I also built an ESM module with this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nl"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/siriwave.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nl"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; 
        &lt;span class="nf"&gt;babel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;node_modules&lt;/span&gt;&lt;span class="cm"&gt;/**’ })
    ]
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We clearly don’t want the &lt;strong&gt;resolve **or **commonjs&lt;/strong&gt; RollupJS plugins because the developer transplier will resolve dependencies for us.&lt;/p&gt;

&lt;p&gt;You can find the final RollupJS configuration here: &lt;a href="https://github.com/kopiro/siriwave/blob/master/rollup.config.js" rel="noopener noreferrer"&gt;https://github.com/kopiro/siriwave/blob/master/rollup.config.js&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Watch and Hot code reload
&lt;/h3&gt;

&lt;p&gt;Using RollupJS, you can also take advantage of &lt;code&gt;rollup-plugin-livereload&lt;/code&gt; and &lt;code&gt;rollup-plugin-serve&lt;/code&gt; plugins to provide a better way to work on scripts.&lt;/p&gt;

&lt;p&gt;Basically, you just add these plugins when you’re in “developer” mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;livereload&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rollup-plugin-livereload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;serve&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rollup-plugin-serve&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;additional_plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;contentBase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="nx"&gt;additional_plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;livereload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We finish by adding these lines into the &lt;strong&gt;package.json:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist/siriwave.m.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsnext:main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist/siriwave.m.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unpkg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist/siriwave.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;amdName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SiriWave&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NODE_ENV=production rollup -c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rollup -c -w&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s clarify some parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;module / jsnext:main&lt;/code&gt; - path of dist ESM module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;unpkg&lt;/code&gt; - path of dist UMD module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;amdName&lt;/code&gt; name of the global object in UMD module&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks a lot &lt;strong&gt;RollupJS!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hope that you find this article interesting, see you soon! 😎&lt;/p&gt;

</description>
      <category>siri</category>
      <category>ios</category>
      <category>web</category>
      <category>canvas</category>
    </item>
    <item>
      <title>How to easily run any Linux tool on any machine</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Thu, 30 Jul 2020 10:41:56 +0000</pubDate>
      <link>https://dev.to/kopiro/how-to-easily-run-any-linux-tool-on-any-machine-2g6p</link>
      <guid>https://dev.to/kopiro/how-to-easily-run-any-linux-tool-on-any-machine-2g6p</guid>
      <description>&lt;p&gt;Have you ever encountered a situation like the ones below?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 1&lt;/strong&gt;: You’re on your Linux workstation, and there is a PHP code that you must execute. But this code only runs under PHP 7, and your workstation only has PHP 5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 2&lt;/strong&gt;: You’re working on your MacBook laptop, and you desperately need your sqlmap tool from your Kali Linux distribution. But you don’t have access to your Virtual Machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 3&lt;/strong&gt;: You’re on your Windows PC, and you immediately need an NGINX server that serves your static files from a directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 4&lt;/strong&gt;: No matter which platform, you have to start your Node.js 10 project. But you don’t have Node.js installed on your platform.&lt;/p&gt;

&lt;p&gt;Or, in general, have you ever been a situation like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation X:&lt;/strong&gt; you are on one platform, and you immediately need a specific Linux tool, without altering your configuration or installing additional software.&lt;/p&gt;

&lt;p&gt;All these situations can be easily solved with a single tool you may have already heard about. It works without messing up your computer by installing additional software, or editing configurations that worked for a long time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt; is an OS-level virtualization system. It can potentially run any binary you have in mind. Furthermore, it can run it in an isolated system, so it can’t touch your files and your precious working configurations.&lt;/p&gt;

&lt;p&gt;All you need is for someone to have already containerized your binary so that you can simply download it as an image. There are already a ton of Docker-built images out there waiting for you.&lt;/p&gt;

&lt;p&gt;Docker does do more than this. It is a platform for developers and system administrators to develop, deploy, and run applications with containers. If you use it only to run your preferred binary, you’re using 1% of its features.&lt;/p&gt;

&lt;p&gt;But let’s start from the beginning.&lt;/p&gt;

&lt;p&gt;You can install Docker on your machine by clicking &lt;a href="https://docs.docker.com/install/overview/" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and selecting your platform from left menu. Then, follow the guide.&lt;/p&gt;

&lt;p&gt;Once you have installed Docker, open your preferred Terminal or Command Prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic concepts
&lt;/h3&gt;

&lt;p&gt;First of all, let’s test if your Docker configuration is working correctly. From the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
Docker version 18.03.0-ce, build 0520e24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Docker is up and running, you should see your version number.&lt;/p&gt;

&lt;p&gt;All you need now is the docker run command.&lt;/p&gt;

&lt;p&gt;The first thing to know is the name of the image you want to use. For official images, you usually have the name of the binary with no additions.&lt;/p&gt;

&lt;p&gt;For example, in the case of PHP, the image name is simply &lt;code&gt;php&lt;/code&gt;. And what about the version? Simple as well, just add the version number (e.g., 7).&lt;/p&gt;

&lt;p&gt;Now let’s run our first container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Situation 1
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You’re on your Linux workstation, and there is a PHP code that you must execute. But this code only runs under PHP 7, and your workstation only has PHP 5.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, now let’s imagine we have this simple code. It only works under PHP 7, because of the spaceship** **operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How we can execute this code with Docker? Let’s build our docker run command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; php:7
&lt;span class="k"&gt;*&lt;/span&gt;Interactive shell
php &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;1&amp;lt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt;
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes — that’s all we need!&lt;/p&gt;

&lt;p&gt;The extra part is the &lt;code&gt;-it&lt;/code&gt; flag, but that’s not difficult. Since we are in the interactive shell, it simply specifies that this container should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;-t (—tty): allocate a &lt;a href="https://unix.stackexchange.com/questions/21147/what-are-pseudo-terminals-pty-tty" rel="noopener noreferrer"&gt;pseudo-TTY&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;-i (—interactive): keep &lt;a href="https://en.wikipedia.org/wiki/Standard_streams" rel="noopener noreferrer"&gt;STDIN&lt;/a&gt; open, even if not attached&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should use them most of the time, with some exceptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Situation 2
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You’re working on your MacBook laptop, and you desperately need your sqlmap tool from your Kali Linux distribution. But you don’t have access to your Virtual Machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately, sqlmap doesn’t have an official simple image name. But maybe someone else has created an image. Let’s search for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker search sqlmap
    NAME                     DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
    paoloo/sqlmap            Dockered sqlmap. Build instructions: https:/…   6
    k0st/alpine-sqlmap       sqlmap on alpine &lt;span class="o"&gt;(&lt;/span&gt;size: ~113 MB&lt;span class="o"&gt;)&lt;/span&gt;                3                                       &lt;span class="o"&gt;[&lt;/span&gt;OK]
    jdecool/sqlmap           sqlmap &lt;span class="o"&gt;(&lt;/span&gt;Automatic SQL injection&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;a contai…   2                                       &lt;span class="o"&gt;[&lt;/span&gt;OK]
    harshk13/kali-sqlmap     Kali Linux base image with Sqlmap               1
    marcomsousa/sqlmap       Simple image that execute Automatic SQL inje…   1                                       &lt;span class="o"&gt;[&lt;/span&gt;OK]
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have several choices. This can happen often. For most cases, the image should be the first one (or the one with the greater star count).&lt;/p&gt;

&lt;p&gt;Let’s use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; paoloo/sqlmap &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;http://localhost]&lt;span class="o"&gt;(&lt;/span&gt;http://localhost&lt;span class="o"&gt;)&lt;/span&gt;
             _
     ___ ___| |_____ ___ ___  &lt;span class="o"&gt;{&lt;/span&gt;1.0.9.32#dev&lt;span class="o"&gt;}&lt;/span&gt;
    |_ -| &lt;span class="nb"&gt;.&lt;/span&gt; | |     | .&lt;span class="s1"&gt;'| . |
    |___|_  |_|_|_|_|__,|  _|
          |_|           |_|   [http://sqlmap.org](http://sqlmap.org)

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user'&lt;/span&gt;s responsibility to obey all applicable &lt;span class="nb"&gt;local&lt;/span&gt;, state and federal laws. Developers assume no liability and are not responsible &lt;span class="k"&gt;for &lt;/span&gt;any misuse or damage caused by this program.
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All arguments that are after [docker run -it {image}]** **are passed to the binary executed in Docker, which is sqlmap in this case.&lt;/p&gt;

&lt;p&gt;Easy enough, right? Yes, but there is a con.&lt;/p&gt;

&lt;p&gt;sqlmap writes log files onto the disk in the &lt;code&gt;~/.sqlmap&lt;/code&gt; path. But since Docker containers run in an isolated environment, we lose everything!!&lt;/p&gt;

&lt;p&gt;This is a feature, but in this case represents a bug for us — let’s fix it.&lt;/p&gt;

&lt;p&gt;To enable persistence so that we don’t lose that log file, we have to create a bind mount between our workstation (host) and the Docker container.&lt;/p&gt;

&lt;p&gt;Let’s decide that our host bind mount directory is &lt;code&gt;/tmp/sqlmap&lt;/code&gt;. This should be an empty directory created only for this purpose!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   /tmp/sqlmap:/root/.sqlmap &lt;span class="se"&gt;\&lt;/span&gt;
   paoloo/sqlmap &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;http://localhost]&lt;span class="o"&gt;(&lt;/span&gt;http://localhost&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the -v option we’ll create a bind mount. The first argument is the host path, and the second is the path on the container that we want to map.&lt;/p&gt;

&lt;p&gt;And, in fact, everything has been saved — including our reports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Situation 3
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You’re on your Windows PC, and you immediately need an NGINX server that serves your static files from a directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you may have noticed, the first time you run docker run, it downloads the images from &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;the Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This could be hundreds of hundreds gigabytes. This is because we downloaded the tag latest of the image (the default).&lt;/p&gt;

&lt;p&gt;But most images have also an ‘alpine’ version of the same image. It uses Linux Alpine OS. This is an optimized version of Linux, which occupies about 130MB.&lt;/p&gt;

&lt;p&gt;Let’s use it in this situation. We know that image name upfront is &lt;code&gt;nginx&lt;/code&gt; (since it is an official image).&lt;/p&gt;

&lt;p&gt;So the final image name will be &lt;code&gt;nginx:alpine&lt;/code&gt;. If you want a specific version (such as 1.14), use &lt;code&gt;nginx:1.14-alpine&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may have more questions. How do we know which directory the NGINX container uses to serve our files? How do we know which port it exposes?&lt;/p&gt;

&lt;p&gt;Luckily, the answers to all your questions are in &lt;a href="https://hub.docker.com/_/nginx/" rel="noopener noreferrer"&gt;the Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, to recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We have to share our directory to serve into the container. Again, this can be done using bind mounts: &lt;code&gt;-v "$(pwd):/usr/share/nginx/html"&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By adding :ro at the end, we are sure that container uses our files in read-only mode.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We must bind the port exposed by the container to the host, and then communicate via TCP on our host: -p 80:80&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/usr/share/nginx/html:ro &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 &lt;span class="se"&gt;\&lt;/span&gt;
   nginx:alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Situation 4
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;No matter which platform, you have to start your Node.js 10 project. But you don’t have Node.js installed on your platform.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perhaps you now understand how it works. Here, we have to share our content and bind ports.&lt;/p&gt;

&lt;p&gt;However, we don’t know the container working directory. Instead, we’re gonna explicitly set it with the -w flag to a custom directory of our choice. For example, you might choose/src — just don’t override an existing directory!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run \
   -p 3000:3000 \
   -v $(pwd):/src \
   -w /src \
   node:10-alpine \
   node main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple and powerful enough?&lt;/p&gt;

&lt;p&gt;Additionally, do you want a ‘shortcut’ to just execute binaries without searching for third-party images?&lt;/p&gt;

&lt;p&gt;Why don’t you try &lt;a href="https://github.com/kopiro/dr" rel="noopener noreferrer"&gt;my simple tool DR&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;I hope that you’re gonna use Docker for all your future binaries! :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to install Kali on a USB stick with pure EFI boot on a Mac (and with virtualization via USB on Virtualbox)</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Thu, 30 Jul 2020 10:24:16 +0000</pubDate>
      <link>https://dev.to/kopiro/how-to-install-kali-on-a-usb-stick-with-pure-efi-boot-on-a-mac-and-with-virtualization-via-usb-on-virtualbox-2md2</link>
      <guid>https://dev.to/kopiro/how-to-install-kali-on-a-usb-stick-with-pure-efi-boot-on-a-mac-and-with-virtualization-via-usb-on-virtualbox-2md2</guid>
      <description>&lt;p&gt;This tutorial is for everyone who wants a USB stick with a &lt;strong&gt;full Kali installation&lt;/strong&gt; to use with your Mac(s). This is not intended to perform a Live Kali installation with persistence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem when you perform a Kali installation on a USB stick is that Kali partitions the disk with the VFAT file system. Mac OS only recognizes HFS+ partitions along with some files needed for it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Your Mac&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A USB stick with Kali ISO installer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A target USB stick, SD card or an SSD external drive where you’re going to install Kali (16GB and USB 3.0 recommended)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This tutorial was heavily inspired by this tutorial with proper fixes for Kali. &lt;a href="https://medium.com/@mmiglier/ubuntu-installation-on-usb-stick-with-pure-efi-boot-mac-compatible-469ad33645c9" rel="noopener noreferrer"&gt;https://medium.com/@mmiglier/ubuntu-installation-on-usb-stick-with-pure-efi-boot-mac-compatible-469ad33645c9&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  USB Live installation
&lt;/h3&gt;

&lt;p&gt;First of all, install Kali on a USB stick by following this &lt;a href="https://docs.kali.org/downloading/kali-linux-live-usb-install" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt;. I’m not gonna bother you on how to proceed on this step, but start here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;KALI_ISO.iso&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/&lt;span class="o"&gt;{&lt;/span&gt;USB&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1m 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you’re ready, reboot your Mac. Insert both your USB sticks, then press ALT and select the &lt;strong&gt;EFI boot&lt;/strong&gt; to start the Live installer.&lt;/p&gt;

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

&lt;p&gt;Kali installer will ask you different questions about your timezone and keyboard layout.&lt;/p&gt;

&lt;p&gt;Proceed until it asks you to partition disks, here select: &lt;strong&gt;Manual.&lt;/strong&gt; Then select your USB &lt;strong&gt;target&lt;/strong&gt; drive (where you want to install Kali). You can recognize by various factors, for example by its size. Click &lt;strong&gt;Continue:&lt;/strong&gt; this will partition your drive.&lt;/p&gt;

&lt;p&gt;Now, back again to the same screen and select the &lt;strong&gt;FREE SPACE&lt;/strong&gt; under the USB target drive. Click &lt;strong&gt;Continue&lt;/strong&gt; and select Automatically partition the free space. Follow the recommended option. Then click on Finish partitioning and write the change to disk.&lt;/p&gt;

&lt;p&gt;The installation process now will copy data to disk. Wait until it finishes (this is gonna take ~30 minutes).&lt;/p&gt;

&lt;h3&gt;
  
  
  Boot from GRUB Live
&lt;/h3&gt;

&lt;p&gt;Once finished, your Mac will reboot and you have to press &lt;strong&gt;ALT&lt;/strong&gt; again. Select &lt;strong&gt;EFI boot&lt;/strong&gt; again.&lt;/p&gt;

&lt;p&gt;What we have to do now is to load our installed Kali system via &lt;strong&gt;Live GRUB&lt;/strong&gt;, because our installed system doesn’t have a recognizable boot-loader by MacOS.&lt;/p&gt;

&lt;p&gt;Once GRUB is loaded, press &lt;strong&gt;c&lt;/strong&gt; to get the GRUB command-line-interface.&lt;/p&gt;

&lt;p&gt;Now you have to understand in which HD is your Kali installation. To do this, when GRUB CLI is loaded, type &lt;code&gt;ls&lt;/code&gt;; eject your USB stick and type &lt;code&gt;ls&lt;/code&gt; gain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grub&amp;gt; &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;memdisk&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hd0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hd1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hd1,gpt3&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hd1, gpt2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hd1,gpt1&lt;span class="o"&gt;)&lt;/span&gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll notice that an &lt;code&gt;hd{X}&lt;/code&gt; disappeared: that is your drive. Now you have to find your &lt;code&gt;gpt&lt;/code&gt;. Probably it’s the &lt;code&gt;gpt2&lt;/code&gt; but just to be sure, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grub&amp;gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hdX,gpt2&lt;span class="o"&gt;)&lt;/span&gt;/boot/grub
unicode.pf2 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If the command says &lt;code&gt;unicode..&lt;/code&gt; it’s the correct gpt; try other **gpts&lt;/em&gt;* otherwise. Now find your UUID of the partition, and annotate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grub&amp;gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;hdX&lt;span class="o"&gt;}&lt;/span&gt;,gpt&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;})&lt;/span&gt;
Partition hd2,gpt2: Filesystem &lt;span class="nb"&gt;type &lt;/span&gt;ext&lt;span class="k"&gt;*&lt;/span&gt; 〈...snip...〉 UUID &lt;span class="k"&gt;**&lt;/span&gt;e86c20b9-83e1-447d-a3be-d1ddaad6c4c6&lt;span class="k"&gt;**&lt;/span&gt; - Partition start at &lt;span class="o"&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can set the parameters to GRUB to boot (use the &lt;strong&gt;tab&lt;/strong&gt; key to use autocomplete):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grub&amp;gt; &lt;span class="nb"&gt;set &lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;hd&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;,gpt&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;})&lt;/span&gt;
grub&amp;gt; linux /boot/vmlinuz〈...tab here!...〉.efi.signed &lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;〈the UUID〉
grub&amp;gt; initrd /boot/initrd〈...tab here!...〉
grub&amp;gt; boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should boot your &lt;strong&gt;Full Kali Installation&lt;/strong&gt; using the Live GRUB. You could differentiate from the Live environment by the password it recognizes during the login process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing the EFI partition
&lt;/h3&gt;

&lt;p&gt;Once you’re logged in in your Kali installation, open the Terminal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fdisk &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and find your drive.&lt;/p&gt;

&lt;p&gt;Now, open &lt;strong&gt;gdisk&lt;/strong&gt; (installed by default on Kali) to partition the drive (be very careful here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gdisk /dev/sd&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;
GPT fdisk &lt;span class="o"&gt;(&lt;/span&gt;gdisk&lt;span class="o"&gt;)&lt;/span&gt; version 1.0.1

Partition table scan:
  MBR: hybrid
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with hybrid MBR&lt;span class="p"&gt;;&lt;/span&gt; using GPT.

Command &lt;span class="o"&gt;(&lt;/span&gt;? &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;delete that EF00 partition&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create a new HFS+ once in its place&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="o"&gt;(&lt;/span&gt;? &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: p
Disk /dev/sdd: ...

&lt;span class="o"&gt;[&lt;/span&gt;...]

Number  Start &lt;span class="o"&gt;(&lt;/span&gt;sector&lt;span class="o"&gt;)&lt;/span&gt;  End &lt;span class="o"&gt;(&lt;/span&gt;sector&lt;span class="o"&gt;)&lt;/span&gt;  Size     Code   Name
   1         2048         1050623  512.0 MiB   EF00   EFI System Partition

&lt;span class="o"&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Just leave defaults values in the sector phase&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Current &lt;span class="nb"&gt;type &lt;/span&gt;is &lt;span class="s1"&gt;'Linux filesystem'&lt;/span&gt;
Hex code or GUID &lt;span class="o"&gt;(&lt;/span&gt;L to show codes, Enter &lt;span class="o"&gt;=&lt;/span&gt; 8300&lt;span class="o"&gt;)&lt;/span&gt;: AF00
Changed &lt;span class="nb"&gt;type &lt;/span&gt;of partition to &lt;span class="s1"&gt;'Apple HFS/HFS+'&lt;/span&gt;

Command &lt;span class="o"&gt;(&lt;/span&gt;? &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!!

Do you want to proceed? &lt;span class="o"&gt;(&lt;/span&gt;Y/N&lt;span class="o"&gt;)&lt;/span&gt;: Y

OK&lt;span class="p"&gt;;&lt;/span&gt; writing new GUID partition table &lt;span class="o"&gt;(&lt;/span&gt;GPT&lt;span class="o"&gt;)&lt;/span&gt; to /dev/sdd.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot.
The operation has completed successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have an unformatted HFS+ partition. To format, we need some tools; but to obtain these tools we need to add the Debian source-list to &lt;strong&gt;apt&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb http://ftp.debian.org/debian unstable main contrib non-free"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apt/sources.list.d/debian.list &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
apt &lt;span class="nb"&gt;install &lt;/span&gt;hfsprogs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can format that partition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkfs.hfsplus /dev/sd&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;1 &lt;span class="nt"&gt;-v&lt;/span&gt; Kali
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have to edit the &lt;strong&gt;/etc/fstab&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gedit /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will launch Gedit. In this file, localize these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /boot/efi was on /dev/sd{X}1 during installation
UUID={XXXXXXX} /boot/efi vfat defaults 0 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and delete them.&lt;/p&gt;

&lt;p&gt;Now, unmount the boot partition, localizing it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount | &lt;span class="nb"&gt;grep&lt;/span&gt; /boot/efi
umount /dev/sd&lt;span class="o"&gt;{&lt;/span&gt;Y&lt;span class="o"&gt;}&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run this to add the necessary entries to your fstab file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UUID=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;blkid &lt;span class="nt"&gt;-o&lt;/span&gt; value &lt;span class="nt"&gt;-s&lt;/span&gt; UUID /dev/sd&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;1&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; /boot/efi auto defaults 0 0"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have to reinstall GRUB so it can use the newly formatted HFS+ partition for its EFI data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /boot/efi/EFI/Kali
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"This file is required for booting"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /boot/efi/EFI/Kali/mach_kernel
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"This file is required for booting"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /boot/efi/mach_kernel
grub-install &lt;span class="nt"&gt;--target&lt;/span&gt; x86_64-efi &lt;span class="nt"&gt;--boot-directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/boot &lt;span class="nt"&gt;--efi-directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/boot/efi &lt;span class="nt"&gt;--bootloader-id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Kali
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then need to “bless” the bootloader code, so that the Mac bootloader will boot it. To do that we need &lt;strong&gt;hfsbless&lt;/strong&gt; binary that is not available via apt. No problem, just clone the repository and build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /root
git clone https://github.com/detly/mactel-boot
&lt;span class="nb"&gt;cd &lt;/span&gt;mactel-boot
make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then bless:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./hfs-bless /boot/efi/EFI/Kali/System/Library/CoreServices/boot.efi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step is to create the grub configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/GRUB_HIDDEN/#GRUB_HIDDEN/g'&lt;/span&gt; /etc/default/grub
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/GRUB_TIMEOUT=10/GRUB_TIMEOUT=0.1/'&lt;/span&gt; /etc/default/grub
grub-mkconfig &lt;span class="nt"&gt;-o&lt;/span&gt; /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfecto! Now reboot and you should see your USB stick in the Mac bootloader by pressing &lt;strong&gt;ALT&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtualizing the USB via Virtualbox
&lt;/h3&gt;

&lt;p&gt;If you ever need to boot this USB stick via Virtualbox (on Mac OSX), there is a simple trick to do that.&lt;/p&gt;

&lt;p&gt;First of all, you’ve to create a VMDK disk that points to the sectors of your USB stick. So, let’s identify that disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diskutil list
    /dev/disk0 (internal):
       #:                       TYPE NAME                    SIZE       IDENTIFIER
       0:      GUID_partition_scheme                         500.3 GB   disk0
       1:                        EFI EFI                     314.6 MB   disk0s1
       2:                 Apple_APFS Container disk1         499.3 GB   disk0s2

    /dev/disk1 (synthesized):
       #:                       TYPE NAME                    SIZE       IDENTIFIER
       0:      APFS Container Scheme -                      +499.3 GB   disk1
                                     Physical Store disk0s2
       1:                APFS Volume Macintosh HD            222.0 GB   disk1s1
       2:                APFS Volume Preboot                 22.4 MB    disk1s2
       3:                APFS Volume Recovery                519.9 MB   disk1s3
       4:                APFS Volume VM                      3.2 GB     disk1s4

    /dev/disk3 (external, physical):
       #:                       TYPE NAME                    SIZE       IDENTIFIER
       0:      GUID_partition_scheme                        *32.0 GB    disk3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case, it is **/dev/disk3. **Let’s unmount before proceeding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diskutil unmountDisk /dev/disk&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With VirtualBox installed, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;VBoxManage internalcommands createrawvmdk &lt;span class="nt"&gt;-filename&lt;/span&gt; ~/Kali.vmdk &lt;span class="nt"&gt;-rawdisk&lt;/span&gt; /dev/disk&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;777 ~/Kali.vmdk
&lt;span class="nb"&gt;chmod &lt;/span&gt;777 /dev/disk&lt;span class="o"&gt;{&lt;/span&gt;X&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfecto. Now, run Virtualbox UI and create a new machine with the following settings:&lt;/p&gt;

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

&lt;p&gt;When VirtualBox asks you for a disk, let’s point to that VMDK created before:&lt;/p&gt;

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

&lt;p&gt;Before starting up the machine, let’s go to Settings and adjust your process counts, video and memory.&lt;/p&gt;

&lt;p&gt;The important things are to set &lt;strong&gt;Enable EFI **under **System &amp;gt; Motherboard.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;This will allow you to boot via EFI. Now start the virtual machine and immediately press &lt;strong&gt;F12&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Boot Maintenance Manager:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Select &lt;strong&gt;Boot from file:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Then select {&lt;strong&gt;SATA_DRIVE} &amp;gt; EFI &amp;gt; Kali &amp;gt; System &amp;gt; Library &amp;gt; CoreServices &amp;gt; boot.efi&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;And, voilà:&lt;/p&gt;

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

&lt;p&gt;Stay tuned :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Kata resolution: Next bigger number with the same digits</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Thu, 30 Jul 2020 10:09:00 +0000</pubDate>
      <link>https://dev.to/kopiro/kata-resolution-next-bigger-number-with-the-same-digits-41mj</link>
      <guid>https://dev.to/kopiro/kata-resolution-next-bigger-number-with-the-same-digits-41mj</guid>
      <description>&lt;p&gt;I would like to share with you my solution of a Kata on CodeWars.&lt;/p&gt;

&lt;p&gt;This is the link to the kata problem: &lt;a href="http://www.codewars.com/kata/next-bigger-number-with-the-same-digits/javascript" rel="noopener noreferrer"&gt;http://www.codewars.com/kata/next-bigger-number-with-the-same-digits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I solved it using Javascript, but the algorithm I created is (of course) extendable to all other programming languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;You have to create a function that takes a positive integer number and returns the next bigger number formed by the same digits.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, just to be clear, let me give you some examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;next bigger of &lt;strong&gt;12&lt;/strong&gt; is &lt;strong&gt;21&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;next bigger of &lt;strong&gt;513&lt;/strong&gt; is &lt;strong&gt;531&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;next bigger of &lt;strong&gt;2017&lt;/strong&gt; is &lt;strong&gt;2071&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;next bigger of &lt;strong&gt;59884848459853&lt;/strong&gt; is &lt;strong&gt;59884848483559&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If no bigger number can be composed using those digits, you have to return &lt;strong&gt;-1&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I approached
&lt;/h3&gt;

&lt;p&gt;Initially, I &lt;strong&gt;totally misunderstood&lt;/strong&gt; the problem, thinking that I should find &lt;strong&gt;the&lt;/strong&gt; biggest number of the same digits… so I simply wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;nextBigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would be all too easy.&lt;/p&gt;

&lt;p&gt;Therefore, I took paper &amp;amp; pencil and I just started writing random numbers.&lt;/p&gt;

&lt;p&gt;I watched for 2–3 minutes, and I realized that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;there is a &lt;strong&gt;left part&lt;/strong&gt; that must be the same (because we want the &lt;strong&gt;next&lt;/strong&gt; bigger number).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;there is a &lt;strong&gt;right part&lt;/strong&gt; that has to change, sorting it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;there is a &lt;strong&gt;pivot&lt;/strong&gt; that is between the two parts and it just increments the number to reach the next.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, the algorithm consists of three parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find the pivot and split the parts
&lt;/h3&gt;

&lt;p&gt;To find the pivot, we read the number from right to left, until we find a digit that is bigger than the previous one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For number 21581957621
2158195 &amp;lt;-- here --&amp;gt; 7621
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case &lt;code&gt;5&lt;/code&gt; is the pivot, because &lt;code&gt;7 &amp;gt; 5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The left part is &lt;code&gt;215819&lt;/code&gt;, the right part is &lt;code&gt;7621&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find the substitute for the pivot
&lt;/h3&gt;

&lt;p&gt;What is our substitute for the pivot?&lt;/p&gt;

&lt;p&gt;It’s pretty simple, remember that we want the next bigger number, so we have to find the &lt;strong&gt;smallest&lt;/strong&gt; digit (in the right part) that is &lt;strong&gt;larger&lt;/strong&gt; than the pivot.&lt;/p&gt;

&lt;p&gt;In this case, &lt;code&gt;6&lt;/code&gt; is our substitute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reorder the right part
&lt;/h3&gt;

&lt;p&gt;Now, to obtain the smallest number, we just reorder the right part, only after inserting our excluded pivot (&lt;code&gt;5&lt;/code&gt;) and remove the substitute (&lt;code&gt;6&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7621+5-6 = 7215 → reorder → 1257
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Join the parts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;215819 + 6 + 1257 = 21581961257
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s all!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Javascript code
&lt;/h2&gt;

&lt;p&gt;The best part is obviously the algorithm, but, here the code I wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;nextBigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// find the pivot, the point (from right) where i &amp;gt; i-1&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// if we are unable to find the pivot, skip&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// splice the digits in the pivot&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// extract pivot&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// find the lowest number &amp;gt; pv&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;mm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mmi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;pv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mm&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;mm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;mm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nx"&gt;mmi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mmi&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mmi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// concat the left + new pivot + right part&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;mm&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>codewars</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What I learned hacking the Facebook Messenger Soccer game</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Thu, 30 Jul 2020 10:07:09 +0000</pubDate>
      <link>https://dev.to/kopiro/what-i-learned-hacking-the-facebook-messenger-soccer-game-mo6</link>
      <guid>https://dev.to/kopiro/what-i-learned-hacking-the-facebook-messenger-soccer-game-mo6</guid>
      <description>&lt;p&gt;Recently, during the last European Football Championship, Facebook introduced a little game in the Messenger app that makes you lose hours and hours despite its simplicity.&lt;/p&gt;

&lt;p&gt;If you didn’t notice it, read &lt;a href="https://mashable.com/2016/06/15/facebook-messenger-soccer-game-how-to/#fmGutFmQ3Oqx" rel="noopener noreferrer"&gt;this article&lt;/a&gt; on Mashable.&lt;/p&gt;

&lt;p&gt;I have to admit… I totally suck at this game, so my best score was &lt;strong&gt;9&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But, as a Developer, the best thing I could do was to beat my friends by hacking the game.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I really thought this would be simple.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  First way: Listen to HTTP(s) requests
&lt;/h3&gt;

&lt;p&gt;While developing apps, you immediately realize that you need an HTTP debugger tool to analyze incoming /outgoing traffic for your APIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.charlesproxy.com/" rel="noopener noreferrer"&gt;Charles&lt;/a&gt; is the best tool I’ve found to accomplish this task. It has a very intuitive interface and you can easily use it for debugging and reverse engineering purposes.&lt;/p&gt;

&lt;p&gt;It was supposed to end at this point: I would have to analyze the API that the Facebook app used and just replay it with CURL while editing the data and the score sent to the server.&lt;/p&gt;

&lt;p&gt;Of course, the API calls are in HTTPS, so they’re encrypted.. but Charles can be used as a man-in-the-middle HTTPS proxy, * enabling you to view in plain text the communication between a web browser and SSL web server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xt04fzwppvubfvdb2jp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xt04fzwppvubfvdb2jp.png" alt="Charles acting as a proxy — with failed requests" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! So I installed the root Charles certificate on the iPhone, and I tried to inspect the traffic; but all HTTP calls to the Facebook servers were denied upfront during the SSL handshake phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah96x4amsxwudruoqb7h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah96x4amsxwudruoqb7h.gif" alt="SSL - image courtesy of cisco.com" width="400" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doing some research, I discovered that some company apps like Facebook and Google use an extra layer of security to ensure that the certificate provided by the remote server is the one that is expected; this technique is called &lt;strong&gt;Certificate Pinning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can easily do this by including the public key of the remote server certificate within the application so that it’s easy to validate the identity of the client for each HTTPS request.&lt;/p&gt;

&lt;p&gt;This technique invalidates the &lt;a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack" rel="noopener noreferrer"&gt;Man in the Middle (MITM)&lt;/a&gt; Attack.&lt;/p&gt;

&lt;p&gt;Great job Facebook! But…(remember, there’s always a but) there is a way to disable the &lt;strong&gt;SSL certificate pinning&lt;/strong&gt; using some system tweaks only available on a jailbroken device.&lt;/p&gt;

&lt;h3&gt;
  
  
  The first way (enhanced): Jailbreak a device and install iOS SSL Kill Switch
&lt;/h3&gt;

&lt;p&gt;My iPhone is currently running iOS 9.x, so at the time of this writing, it was impossible to jailbreak. So I took an old iPad mini running iOS 8.3.x and easily jailbroke it using the &lt;a href="http://www.taig.com/en/" rel="noopener noreferrer"&gt;TaiG&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;Searching on the web, I found &lt;a href="https://github.com/nabla-c0d3/ssl-kill-switch2" rel="noopener noreferrer"&gt;SSL Kill Switch 2&lt;/a&gt;, a Blackbox tool to disable SSL certificate validation within iOS and OS X apps.&lt;/p&gt;

&lt;p&gt;Once loaded into an iOS or OS X App, SSL Kill Switch 2 patches specific low-level SSL functions within the Secure Transport API in order to &lt;em&gt;override, and disable the system’s default certificate validation as well as any kind of custom certificate validation&lt;/em&gt; (such as certificate pinning).&lt;/p&gt;

&lt;p&gt;The SSL Kill Switch uses &lt;a href="http://iphonedevwiki.net/index.php/MobileSubstrate" rel="noopener noreferrer"&gt;MobileSubstrate&lt;/a&gt; to patch system functions like the &lt;a href="https://developer.apple.com/library/ios/DOCUMENTATION/Security/Reference/secureTransportRef/Reference/reference.html" rel="noopener noreferrer"&gt;Secure Transport API&lt;/a&gt;. They are the lowest-level TLS implementation on iOS.&lt;/p&gt;

&lt;p&gt;This means that disabling SSL certificate validation in the Secure Transport API should affect most (if not all) of the network APIs available within the iOS framework.&lt;/p&gt;

&lt;p&gt;Please, do yourself a favor and follow &lt;a href="https://nabla-c0d3.github.io/" rel="noopener noreferrer"&gt;this blog&lt;/a&gt; that covers all these concepts.&lt;/p&gt;

&lt;p&gt;So, I connected to the iPad using SSH and installed the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="o"&gt;[&lt;/span&gt;https://github.com/nabla-c0d3/ssl-kill-switch2/releases/download/0.10/com.nablac0d3.SSLKillSwitch2_0.10.deb]&lt;span class="o"&gt;(&lt;/span&gt;https://github.com/nabla-c0d3/ssl-kill-switch2/releases/download/0.10/com.nablac0d3.SSLKillSwitch2_0.10.deb&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--no-check-certificate&lt;/span&gt;
dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;com.nablac0d3.SSLKillSwitch2_0.10.deb]&lt;span class="o"&gt;(&lt;/span&gt;https://github.com/nabla-c0d3/ssl-kill-switch2/releases/download/0.10/com.nablac0d3.SSLKillSwitch2_0.10.deb&lt;span class="o"&gt;)&lt;/span&gt;
killall &lt;span class="nt"&gt;-HUP&lt;/span&gt; SpringBoard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once rebooted, I expected to see the plain traffic, but it was an optimistic vision: &lt;em&gt;I got the same errors.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I tried this way for another hour. I read somewhere that Facebook and Twitter use the SPDY protocol for their API calls, and this could be a problem for Charles. So I installed another tweak that (theoretically) disabled the SPDY protocol, but it didn’t work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starving.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Looking at the project issues, I noticed that someone else had the same problem (&lt;a href="https://github.com/nabla-c0d3/ssl-kill-switch2/issues/13" rel="noopener noreferrer"&gt;https://github.com/nabla-c0d3/ssl-kill-switch2/issues/13&lt;/a&gt;), with no resolution.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Pause.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Second way: simulate touch events within the application
&lt;/h3&gt;

&lt;p&gt;I realized that there are many game cheats that use a “human” approach: &lt;em&gt;simulate touch events&lt;/em&gt; (one of the most popular games that many game cheats utilize this strategy on is Clash of Clans).&lt;/p&gt;

&lt;p&gt;Browsing the web for a tool that automates these operations, I found this awesome tweak - &lt;a href="https://autotouch.net/" rel="noopener noreferrer"&gt;AutoTouch&lt;/a&gt;. It can record human touch events and store the data in a LUA script. You can then edit this produced script and simulate whatever you want anywhere on your device.&lt;/p&gt;

&lt;p&gt;Once installed with &lt;a href="https://cydia.saurik.com/" rel="noopener noreferrer"&gt;Cydia&lt;/a&gt;, I saved a BMP screenshot of the Messenger application with the ball visible and obtained the coordinates of where to click.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8zm5tbkzj4zyx37xp80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8zm5tbkzj4zyx37xp80.png" alt="Screenshot made to obtain the coordinate" width="768" height="331"&gt;&lt;/a&gt;&lt;em&gt;Screenshot made to obtain the coordinate&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What I thought is that, by clicking &lt;em&gt;exactly&lt;/em&gt; in the center of the x-axis of the ball, I only had to simulate repetitive touch events in the same coordinates and then stop the script when I had a score that I was satisfied with.&lt;/p&gt;

&lt;p&gt;Here’s what I wrote to accomplish this goal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    adaptResolution(768, 1024);
    adaptOrientation(ORIENTATION_TYPE.PORTRAIT);

    for i=1,2000 do

      touchDown(1, 544, 954);
      usleep(66000);
      touchUp(1, 544, 954);

      usleep(10000);

    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nope, it didn’t work.&lt;/p&gt;

&lt;p&gt;Probably, Facebook developers introduced a random error on touch coordinates to better simulate the game, or to prevent these type of scripts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Or, maybe I just clicked at the wrong pixel.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, for a second chance, I tried to simulate multiple clicks in a larger area, but without luck. Sometimes, I simulated so many touch events that the Springboard &lt;em&gt;just crashed *because of memory errors&lt;/em&gt;.*&lt;/p&gt;

&lt;center&gt;&lt;/center&gt;

&lt;p&gt;Instead of clicking on the same coordinates every time, I tried a better approach.&lt;/p&gt;

&lt;p&gt;Reading the AutoTouch &lt;a href="https://autotouch.net/server/doc/en.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, I found the following two methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;findColor (color, count, region) - Search the coordinates of the pixel points matching the specified color on the current screen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getColor (x, y) - Get the color value of the pixel point of the specified coordinate on the current screen.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea was to find a unique color inside the ball and use the &lt;em&gt;findColor&lt;/em&gt; method to get the coordinates of the ball at that moment, to simulate a touch event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    adaptResolution(768, 1024);
    adaptOrientation(ORIENTATION_TYPE.PORTRAIT);

    local c = getColor(544, 954);

    for i=1,2000 do
      local r = findColor(c, 0, {400, 500, 768, 1024});

      for i, v in pairs(r) do
        touchDown(1, v[1], v[2]);
        usleep(66000);
        touchUp(1, v[1], v[2]);
        usleep(10000);
      end

    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t know why, but it simply didn’t work. Maybe the &lt;em&gt;findColor&lt;/em&gt; is too slow to intercept the ball, which then makes the script useless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Third way: Reverse engineer the app
&lt;/h3&gt;

&lt;p&gt;I don’t have good native skills in Objective C, but I remember (when I played with the jailbreak ~4 years ago) that there was a tool by &lt;a href="https://twitter.com/saurik?lang=en" rel="noopener noreferrer"&gt;Saurik&lt;/a&gt; that could inject itself into iOS processes.&lt;/p&gt;

&lt;p&gt;It is released along with Cydia and was called &lt;a href="http://www.cycript.org/" rel="noopener noreferrer"&gt;Cycript&lt;/a&gt;. It allowed developers to explore and modify running applications on iOS, by injecting code at run time.&lt;/p&gt;

&lt;p&gt;I read some basic tutorials on how to use it, and after a few struggles, I decided to follow this (another) way.&lt;/p&gt;

&lt;p&gt;Once you login via SSH into your iOS device, you can easily attach to a process just by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cycript &lt;span class="nt"&gt;-p&lt;/span&gt; Messenger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried to inspect some basic UI classes like &lt;em&gt;UIApp&lt;/em&gt;, but didn’t find anything interesting. Then I made a complete &lt;strong&gt;class dump&lt;/strong&gt;, filtering it for the keyword &lt;strong&gt;soccer.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var C = Object.keys(ObjectiveC.classes);
var soccer_classes = []; 
for (var i = 0; i &amp;lt; C.length; i++)
   C[i].match(/soccer/i) &amp;amp;&amp;amp; soccer_classes.push( C[i] );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was a slow process.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://www.reddit.com/r/programming/comments/3h52yk/someone_discovered_that_the_facebook_ios/" rel="noopener noreferrer"&gt;discovered&lt;/a&gt; that Facebook Messenger has a very large number of classes.&lt;/p&gt;

&lt;p&gt;But, in the end, I got a small list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcowwz91lggi2aebeq1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcowwz91lggi2aebeq1v.png" alt="Output of the script" width="713" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I obtained the class names, I used a script to print all methods of the class, and, by inspecting the &lt;strong&gt;MNSoccerGame&lt;/strong&gt; class, the resulting methods were:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fosehujnduhm5herj5b8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fosehujnduhm5herj5b8c.png" alt="The methods dump of the MNSoccerGame class" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I still don’t understand what is the method &lt;code&gt;wasCheatDetected.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that I had a complete list of the class methods, I decided to override the *_setScore *method, hoping that other methods didn’t notice that.&lt;/p&gt;

&lt;p&gt;To do this, I used the &lt;strong&gt;MobileSubstrate&lt;/strong&gt; and its &lt;strong&gt;MS.hookMessage&lt;/strong&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import com.saurik.substrate.MS; 

var _setScore_pointer = {}; 
MS.hookMessage(MNSoccerGame, @selector(_setScore:), function(arg0) {
      return _setScore_pointer-&amp;gt;call(this, 9999); 
}, _setScore_pointer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can just play, &lt;strong&gt;lose&lt;/strong&gt;, and &lt;strong&gt;anyway score a new record.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I learned
&lt;/h3&gt;

&lt;p&gt;Never stop yourself. Always try and discover a new way to accomplish the same thing. I know, it’s just a game, but if you treat the problem you’re trying to solve like a challenge, you’ll get much more than the satisfaction of beating your friends.&lt;/p&gt;

</description>
      <category>hacking</category>
      <category>facebook</category>
      <category>cycrypt</category>
      <category>ios</category>
    </item>
    <item>
      <title>The hardware behind Otto: a monkey plush which became my vocal assistant</title>
      <dc:creator>Flavio De Stefano</dc:creator>
      <pubDate>Thu, 30 Jul 2020 09:55:44 +0000</pubDate>
      <link>https://dev.to/kopiro/the-hardware-behind-otto-a-monkey-plush-which-became-my-vocal-assistant-1gaa</link>
      <guid>https://dev.to/kopiro/the-hardware-behind-otto-a-monkey-plush-which-became-my-vocal-assistant-1gaa</guid>
      <description>&lt;p&gt;Otto is a monkey plush that we found in a highway store during a trip with my girlfriend in February 2017.&lt;/p&gt;

&lt;p&gt;Its ability, while being extremely cute, was to listen to you, then walk and repeat all things with a higher pitch.&lt;/p&gt;

&lt;p&gt;My goal was to make it more powerful by transforming it into a vocal assistant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftouv3fj0hmok53pm0jve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftouv3fj0hmok53pm0jve.png" alt="SkeletOtto and Otto" width="800" height="534"&gt;&lt;/a&gt;&lt;em&gt;SkeletOtto and Otto&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the first part of a series about Otto.&lt;/p&gt;

&lt;p&gt;Originally, it was composed by the following hardware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A motor connected to his legs for allowing him to walk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A simple, closed (for modifications), built-in board&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A microphone and a speaker&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A push button to start the listening phase&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Four AA batteries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A switch to completely power off the circuit&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to replace all these things with fresh and &lt;strong&gt;programmable&lt;/strong&gt; hardware.&lt;/p&gt;

&lt;p&gt;The real challenge here was to find the rights components that fitted in the original enclosure. The available space wasn’t much, so every choice had to be done conscientiously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Base board
&lt;/h3&gt;

&lt;p&gt;The preferred hardware for this project are Raspberry PI boards.&lt;/p&gt;

&lt;p&gt;They are tiny and powerful enough to allow developers to use a high-level programming language and built-in libraries without flashing the software every-time.&lt;/p&gt;

&lt;p&gt;Furthermore, you can debug your application in a more comfortable environment.&lt;/p&gt;

&lt;p&gt;The best hardware at the time was the Raspberry Pi Zero W. Launched at the end of February 2017, the Pi Zero W has all the functionality of the original Pi Zero but with added connectivity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8r9n12qysbb2th3v5kt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8r9n12qysbb2th3v5kt.png" alt="Raspberry Pi Zero W" width="800" height="450"&gt;&lt;/a&gt;&lt;em&gt;Raspberry Pi Zero W&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The board was not enough for such a project, so I added additional hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Audio components
&lt;/h3&gt;

&lt;p&gt;To build a vocal assistant, we need audio components. The requirements for these components are, of course, a speaker and a microphone.&lt;/p&gt;

&lt;p&gt;For the microphone, I tried a USB microphone. The problem with this accessory was that it was not as sensible as I wished. Furthermore, an additional USB hub was required to connect it.&lt;/p&gt;

&lt;p&gt;Moreover, I couldn’t connect a raw speaker easily.&lt;/p&gt;

&lt;p&gt;For this reason, I opted to buy an additional board that accomplished this task very well: &lt;a href="https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT-p-2874.html" rel="noopener noreferrer"&gt;ReSpeaker 2-Mics Pi HAT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ReSpeaker 2-Mics Pi HAT is a dual-microphone expansion board for Raspberry Pi designed for AI and voice applications.&lt;/p&gt;

&lt;p&gt;The board is developed based on WM8960, a low power stereo codec. There are 2 microphones on both sides of the board for collecting sounds. It also provides 3 APA102 RGB LEDs, 1 User Button, and 2 on-board Grove interfaces.&lt;/p&gt;

&lt;p&gt;I did not plan to connect LEDs to my board, but the fact that this HAT has built-in LEDs made me think to use them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7q268porqu3yzm6ji3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7q268porqu3yzm6ji3g.png" alt="ReSpeaker 2-Mics Pi HAT — Hardware specifications" width="800" height="463"&gt;&lt;/a&gt;&lt;em&gt;ReSpeaker 2-Mics Pi HAT — Hardware specifications&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then I took an old Bluetooth mini-speaker, disassembled it, and connected it to the JST 2.0 Speaker Out port.&lt;/p&gt;

&lt;p&gt;To make it work, you have to install their drivers on your board. Drivers are also used to control LEDs within your application via a standard protocol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft98l0ve7g5d6258ps6w2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft98l0ve7g5d6258ps6w2.png" alt="ReSpeaker 2-Mics Pi HAT" width="700" height="525"&gt;&lt;/a&gt;&lt;em&gt;ReSpeaker 2-Mics Pi HAT&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hint: when you install a shield, all your GPIO pins are covered. It’s useful to know which pins are **really used&lt;/em&gt;* by your board. To accomplish that, use &lt;a href="https://pinout.xyz/" rel="noopener noreferrer"&gt;https://pinout.xyz/&lt;/a&gt;*&lt;/p&gt;

&lt;p&gt;For example, for this board, check out this link: &lt;a href="https://pinout.xyz/pinout/respeaker_2_mics_phat" rel="noopener noreferrer"&gt;https://pinout.xyz/pinout/respeaker_2_mics_phat&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Powering the board
&lt;/h3&gt;

&lt;p&gt;The Raspberry Pi board can be easily powered via USB 5V input. The problem with this approach is that you have to buy a battery pack and connect it via USB.&lt;/p&gt;

&lt;p&gt;I didn’t find any battery pack small enough to fit my plush, then my unique alternative was using LiPo batteries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ixlf0k46h6gminxy3xy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ixlf0k46h6gminxy3xy.png" alt="LiPo Battery — 3.7V 2000mAh" width="800" height="800"&gt;&lt;/a&gt;&lt;em&gt;LiPo Battery — 3.7V 2000mAh&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can’t connect your LiPo battery to your board, you have to use a converter. It can be powered by any 3.7V LiIon/LiPoly battery, and then it converts the battery output to 5.2V DC.&lt;/p&gt;

&lt;p&gt;Initially, I bought a &lt;a href="https://shop.pimoroni.com/products/lipo-shim" rel="noopener noreferrer"&gt;LiPo SHIM&lt;/a&gt;, but I didn’t notice that this controller provides power to your board without charging your batteries.&lt;/p&gt;

&lt;p&gt;For this reason, I switched to &lt;a href="https://shop.pimoroni.com/products/powerboost-500-charger-rechargeable-5v-lipo-usb-boost-500ma" rel="noopener noreferrer"&gt;Adafruit PowerBoost 500 Charger.&lt;/a&gt;** It has a built-in battery charger circuit. You’ll be able to keep your project running even while charging the battery!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv93b6ogsmwbfnx53cwxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv93b6ogsmwbfnx53cwxg.png" alt="Adafruit PowerBoost 500 Charger" width="500" height="500"&gt;&lt;/a&gt;&lt;em&gt;Adafruit PowerBoost 500 Charger&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional hardware
&lt;/h3&gt;

&lt;p&gt;The software uses the “hot-word” concept to start the interaction. Basically, it continuously listens for a hot-word, like “Hey Otto”, then you just talk and say commands.&lt;/p&gt;

&lt;p&gt;For having an alternative method to start the interaction, I installed a** push-button **connected directly to the GPIO board, at the GPIO8 pin.&lt;/p&gt;

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

&lt;p&gt;Now only one thing was missing: &lt;strong&gt;the power on-off switch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I connected this simple component to the PowerBoost Charger via its ENABLE port. The purpose of the ENABLE port is to disconnect the output completely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zrjfyph124kwtmptrhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zrjfyph124kwtmptrhq.png" alt="Power on-off switch" width="130" height="130"&gt;&lt;/a&gt;&lt;em&gt;Power on-off switch&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect everything together
&lt;/h3&gt;

&lt;p&gt;Here you can see in details the complete circuit diagram (&lt;a href="https://www.circuit-diagram.org/circuits/0d85ce05" rel="noopener noreferrer"&gt;https://www.circuit-diagram.org/circuits/0d85ce05&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvy56r1tv66qq70vtguh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvy56r1tv66qq70vtguh0.png" alt="Otto Circuit diagram" width="800" height="493"&gt;&lt;/a&gt;&lt;em&gt;Otto Circuit diagram&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here a snapshot of the work:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8992%2F1%2AobNKyfbvgL3dUcVR3tK-6A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8992%2F1%2AobNKyfbvgL3dUcVR3tK-6A.jpeg" alt="The hardware behind Otto" width="800" height="533"&gt;&lt;/a&gt;&lt;em&gt;The hardware behind Otto&lt;/em&gt;&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>electronics</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
