<?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: Nick Gottschlich</title>
    <description>The latest articles on DEV Community by Nick Gottschlich (@nickgottschlich).</description>
    <link>https://dev.to/nickgottschlich</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%2F450469%2Fe3678999-1837-4e13-8a68-3ee968d60e3f.jpg</url>
      <title>DEV Community: Nick Gottschlich</title>
      <link>https://dev.to/nickgottschlich</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nickgottschlich"/>
    <language>en</language>
    <item>
      <title>Build your SO their own "attention button" this Valentine's Day</title>
      <dc:creator>Nick Gottschlich</dc:creator>
      <pubDate>Mon, 01 Feb 2021 04:01:52 +0000</pubDate>
      <link>https://dev.to/nickgottschlich/build-your-so-their-own-attention-button-this-valentine-s-day-318e</link>
      <guid>https://dev.to/nickgottschlich/build-your-so-their-own-attention-button-this-valentine-s-day-318e</guid>
      <description>&lt;p&gt;Valentine's day is coming up! It's time to start thinking about what you are going to get for that special somebody. But everything just feels so played out! Flowers, chocolate, jewelry, it's all just so gauche you say.&lt;/p&gt;

&lt;p&gt;No, this Valentine's day you want to give them something special. Something that comes from the heart. Something personal you've built with your own blood, sweat and tears! And something cheap, because you just spent all your money on GameStop stock. Well you're in luck, because with just a raspberry pi and a few spare parts I'm going to teach you how to build your SO their very own attention button. This magical button will, at their simple press, summon you for whatever type of attention your special someone needs. Sound great, right?&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  Parts needed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi - &lt;a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/"&gt;https://www.raspberrypi.org/products/raspberry-pi-4-model-b/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Raspberry Pi Power Supply (if one didn't come with your kit) - &lt;a href="https://www.raspberrypi.org/products/type-c-power-supply/"&gt;https://www.raspberrypi.org/products/type-c-power-supply/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Breadboard - &lt;a href="https://www.amazon.com/DEYUE-breadboard-Set-Prototype-Board/dp/B07LFD4LT6"&gt;https://www.amazon.com/DEYUE-breadboard-Set-Prototype-Board/dp/B07LFD4LT6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Breadboard cables - &lt;a href="https://www.amazon.com/EDGELEC-Breadboard-Optional-Assorted-Multicolored/dp/B07GD2BWPY/132-5436773-5950636?psc=1"&gt;https://www.amazon.com/EDGELEC-Breadboard-Optional-Assorted-Multicolored/dp/B07GD2BWPY/132-5436773-5950636?psc=1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Breadboard buttons - &lt;a href="https://www.amazon.com/Tactile-Momentary-Assortment-Kit-200-Switches/dp/B0723BG637"&gt;https://www.amazon.com/Tactile-Momentary-Assortment-Kit-200-Switches/dp/B0723BG637&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Assembly
&lt;/h3&gt;

&lt;p&gt;Set up your raspberry pi! See &lt;a href="https://projects.raspberrypi.org/en/projects/raspberry-pi-getting-started"&gt;https://projects.raspberrypi.org/en/projects/raspberry-pi-getting-started&lt;/a&gt; for instructions. You will likely need to be able to hook up your Pi to a keyboard, mouse and monitor to get this to work. Once you've booted to the Raspbian desktop you're good to go.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hooking up Pi
&lt;/h4&gt;

&lt;p&gt;Now, you'll need to hook up your Raspberry Pi to your push button. This is pretty simple to do! Get out your breadboard, push button, and two male-to-female breadboard cables.&lt;/p&gt;

&lt;p&gt;On the raspberry pi, connect your cables to ground (pin 6) and GPIO 2 (pin 3). See &lt;a href="https://pinout.xyz/#"&gt;https://pinout.xyz/#&lt;/a&gt; for details. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3iYo5M9I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/g4eoqn3wt5f0eaolcrft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3iYo5M9I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/g4eoqn3wt5f0eaolcrft.png" alt="Cables connected to Raspberry Pi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: You can actually use any GPIO button you want, I just went with GPIO 2&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Hooking up breadboard
&lt;/h4&gt;

&lt;p&gt;Now, take those cables and connect them to your breadboard. They should be placed vertically, one spot apart on the column, and then the button should be placed on the same rows as the two cables.&lt;/p&gt;

&lt;p&gt;See this diagram I borrowed from &lt;a href="https://roboticsbackend.com/raspberry-pi-gpio-interrupts-tutorial/"&gt;here&lt;/a&gt;. &lt;em&gt;Ignore the placement of the gpio pin, just pay attention to the breadboard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AvEJl2u5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zlq5wcurqcfwx48fefnj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AvEJl2u5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zlq5wcurqcfwx48fefnj.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2f2DYv7d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vlkmuzimwo7vhsh6txir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2f2DYv7d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vlkmuzimwo7vhsh6txir.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it! You now have your hardware wired up!&lt;/p&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;

&lt;p&gt;You can either write this code directly on the Raspberry pi using the text editor and terminal, or you can SSH into your Pi and use a terminal on a different computer. To do that, first find the IP of your pi using &lt;code&gt;hostname -I&lt;/code&gt; and then on another computer &lt;code&gt;ssh pi@IP_ADDRESS&lt;/code&gt; and enter your password. I highly recommend using something like &lt;a href="https://code.visualstudio.com/docs/remote/ssh"&gt;VS Code SSH&lt;/a&gt; to develop on Pi.&lt;/p&gt;

&lt;h4&gt;
  
  
  Registering button press
&lt;/h4&gt;

&lt;p&gt;The code we will write will be in Python. Let's start with just getting the button to register. We will be using the &lt;a href="https://github.com/gpiozero/gpiozero"&gt;gpiozero library&lt;/a&gt; to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gpiozero&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Button&lt;/span&gt;

&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Button&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="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_pressed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'button pressed!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wow, that was easy! If you've used a different GPIO pin then I did (remember to check &lt;a href="https://pinout.xyz/#"&gt;https://pinout.xyz/#&lt;/a&gt; !) just use that number in the place of where I wrote "2". Save your file as &lt;code&gt;attn_button.py&lt;/code&gt; and run your code with &lt;code&gt;python attn_button.py&lt;/code&gt; (or &lt;code&gt;python3 attn_button.py&lt;/code&gt; if needed).&lt;/p&gt;

&lt;p&gt;Now press the button on your Pi and watch the text appear in your terminal! You've got your button wired up!&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending a notification:
&lt;/h3&gt;

&lt;p&gt;A button is no good if it doesn't do anything, right? So now we need the button to ping your phone whenever &lt;del&gt;the user&lt;/del&gt; your special smookums smashes that button. I decided to leverage the twitter API for this as it made it easy to send notifications to my phone, but there are limitless ways to go about this. Try experimenting with something like &lt;a href="https://ifttt.com/"&gt;https://ifttt.com/&lt;/a&gt; or even developing your own Android or iOS app if you want to get creative!&lt;/p&gt;

&lt;p&gt;The way I did it was by creating a new twitter account which then will DM my &lt;a href="https://twitter.com/NickGottschlich"&gt;main twitter account&lt;/a&gt; (see how I snuck that in there) whenever the button is pressed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up the twitter API
&lt;/h4&gt;

&lt;p&gt;Head to &lt;a href="https://developer.twitter.com"&gt;https://developer.twitter.com&lt;/a&gt; and set up the API on whatever account will be sending the DM. You then will need to create a new project from the &lt;a href="https://developer.twitter.com/en/portal/projects-and-apps"&gt;dashboard&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hLKiQySd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e15cfwm2g38u816zxvlw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hLKiQySd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e15cfwm2g38u816zxvlw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will lead you through a quick process.&lt;/p&gt;

&lt;p&gt;Once you finish, you will need to copy your API key and API secret key to your python file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;twitter_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'KEY'&lt;/span&gt;
&lt;span class="n"&gt;twitter_api_key_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'SECRET_KEY'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, head to the "app settings" of the app that was created and click the "keys":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yw1K1OCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xd34uulnfrupcyjxfcng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yw1K1OCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xd34uulnfrupcyjxfcng.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click "generate" next to access token and secret and copy those over to your python script as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;twitter_access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'ACCESS_TOKEN'&lt;/span&gt;
&lt;span class="n"&gt;twitter_access_token_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'ACCESS_TOKEN_SECRET'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Almost there, I promise! Now, install the python twitter api wrapper called tweepy by running &lt;code&gt;pip install tweepy&lt;/code&gt; or &lt;code&gt;pip3 install tweepy&lt;/code&gt; and add &lt;code&gt;import tweepy&lt;/code&gt; to the top of your file.&lt;/p&gt;

&lt;p&gt;Finally, get the ID of the twitter account you will be sending the message to using &lt;a href="https://tweeterid.com/"&gt;https://tweeterid.com/&lt;/a&gt;. Obviously, you will need to be logged into this account on a mobile device you carry around and have notifications for direct messages turned on.&lt;/p&gt;

&lt;p&gt;Now, here is your final code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gpiozero&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Button&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tweepy&lt;/span&gt;

&lt;span class="n"&gt;twitter_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'API'&lt;/span&gt;
&lt;span class="n"&gt;twitter_api_key_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'API_SECRET'&lt;/span&gt;
&lt;span class="n"&gt;twitter_access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'ACCESS_TOKEN'&lt;/span&gt;
&lt;span class="n"&gt;twitter_access_token_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'ACCESS_TOKEN_SECRET'&lt;/span&gt;

&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Button&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="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_pressed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'button pressed!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OAuthHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter_api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;twitter_api_key_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;twitter_access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;twitter_access_token_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_direct_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'TWITTER_ACCT_ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Yo! Your SO needs attention! Get to it!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&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="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Phew! That was a lot, but now we are ready! Take a read through this code and you'll find it's pretty self explanatory. &lt;code&gt;tweepy.OAuthHandler&lt;/code&gt; and &lt;code&gt;auth.set_access_token&lt;/code&gt; get access to twitter, and &lt;code&gt;send_direct_message&lt;/code&gt; sends a message. The &lt;code&gt;time&lt;/code&gt; functions are just there to prevent the code from sending a whole bunch of DMs at once. Easy, right!&lt;/p&gt;

&lt;p&gt;Go ahead and run the script and give it a test, with the script running, you should be able to press the button and very quickly get a ding on your phone indicating a twitter DM has just arrived. That's it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping the script running.
&lt;/h3&gt;

&lt;p&gt;Now, one last thing, you've gotta make sure the script stays running all the time. There's an easy way to do this. You are going to use linux's &lt;a href="https://www.howtogeek.com/662422/how-to-use-linuxs-screen-command/"&gt;screen command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In an SSH'd terminal to your Pi: &lt;code&gt;screen -S attn-button&lt;/code&gt; then run your file with &lt;code&gt;python attn_button.py&lt;/code&gt;. That's it! You can close the terminal and your python script will still be running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Congrats, you're done!
&lt;/h3&gt;

&lt;p&gt;You now have a working attention button. Nice work! Your special someone is going to love it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;Why not try adding multiple buttons that can send multiple messages? Maybe try 3d printing a more professional looking button then the sketchy looking breadboard? There are all kinds of ways to extend this project.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>raspberrypi</category>
      <category>pi</category>
    </item>
    <item>
      <title>JavaSpooky: Using JavaScript to revive an old horror webcomic from a flashplayer grave.</title>
      <dc:creator>Nick Gottschlich</dc:creator>
      <pubDate>Sat, 31 Oct 2020 19:47:40 +0000</pubDate>
      <link>https://dev.to/nickgottschlich/javaspooky-using-javascript-to-revive-an-old-horror-webcomic-from-a-flashplayer-grave-4dgb</link>
      <guid>https://dev.to/nickgottschlich/javaspooky-using-javascript-to-revive-an-old-horror-webcomic-from-a-flashplayer-grave-4dgb</guid>
      <description>&lt;p&gt;Back in the day, a certain &lt;a href="https://comic.naver.com/webtoon/detail.nhn?titleId=350217&amp;amp;no=31&amp;amp;weekday=tue" rel="noopener noreferrer"&gt;internet webcomic&lt;/a&gt; achieved a level of infamy for it's startling twist.&lt;/p&gt;

&lt;p&gt;However, the shocking twist that powered that comic was written with &lt;a href="https://en.wikipedia.org/wiki/Adobe_Flash_Player" rel="noopener noreferrer"&gt;Adobe Flash Player&lt;/a&gt;, which will be completely deprecated from the internet on December 31, 2020.&lt;/p&gt;

&lt;p&gt;And thus, a part of internet history was lost to the ages...&lt;/p&gt;

&lt;p&gt;Or was it? (BE WARNED, the below link contains spooky jump scares)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thatspookycomic.github.io/" rel="noopener noreferrer"&gt;https://thatspookycomic.github.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Were you brave enough to try it out? I decided to recreate the infamous effect using nothing but vanilla JS, HTML, and CSS along with some new-school web APIs. You can compare the results of the page above with a &lt;a href="https://www.youtube.com/watch?v=_vI0Wmxc2cY" rel="noopener noreferrer"&gt;recording of the original effect&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how was it done?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Jump Scare sound
&lt;/h3&gt;

&lt;p&gt;Let's start from the top of the page. You probably noticed the pumpkin emoji that the page tells you to click on, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9btja8yeb2nvtaqq311h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9btja8yeb2nvtaqq311h.png" alt="Pumpkin Emoji with some text telling you to click it"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems like a cute little innocent Halloween fun, right? Well, it actually hides a sinister purpose.&lt;/p&gt;

&lt;p&gt;In order to play the jump scare noises on the page, I use a web API called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement/Audio" rel="noopener noreferrer"&gt;Audio&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now, the Audio tool is designed to prevent the annoyance of auto-playing media that starts when a webpage is opened. If the user has not interacted with the document, when you try to play a sound you get this error: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8s1nl7jcko2fpc5e00yu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8s1nl7jcko2fpc5e00yu.png" alt="Dev Tools console error saying user has to interact with document before audio will play"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in order for us to do something like:&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;spookySound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audio/spooky-sound.mp3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;spookySound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We first have to incentive the user to click somewhere on the page! So I dropped in a little JS to update a pumpkin emoji onclick:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"clickEmoji"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"emojiClickChange()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🎃&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;


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

&lt;/div&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;emojiClickChange&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clickEmoji&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;We are now ready to fire off some scary sounds!&lt;/p&gt;

&lt;h3&gt;
  
  
  Triggering the Jump Scare
&lt;/h3&gt;

&lt;p&gt;Next up, we need to detect when the user has scrolled to a certain point on the page, where we need to fire the jump scare effect.&lt;/p&gt;

&lt;p&gt;To do that, we use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;Intersection Observer API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I dropped an id on the two images where I wanted the jump scare to occur:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"images/7.jpg"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"firstTarget"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"images/17.jpg"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"secondTarget"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;and then I create an observer and observe those images:&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;let&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firstTarget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondTarget&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;Now, we can create a function that will be called whenever the state of those observed items changes!&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;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&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;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firstTarget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;firstJumpScareActivated&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;playFirstJumpScare&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Whenever any of the observed items changes state the &lt;code&gt;handler&lt;/code&gt; function is called. We can read &lt;code&gt;entry.target.id&lt;/code&gt; to know what item we are dealing with, &lt;code&gt;isIntersecting&lt;/code&gt; to know if the user has scrolled the item into view, and I use a boolean &lt;code&gt;XJumpScareActivated&lt;/code&gt; to make sure the effect only happens once.&lt;/p&gt;

&lt;p&gt;One last thing, if your images load in slow, the effect might be triggered if the id is briefly in frame when the page loads. The right way to fix this is to wait for all images to be loaded before starting the intersection observer, but for the sake of simplicity, a setTimeout did the job for me:&lt;/p&gt;

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

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firstTarget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondTarget&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="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Great, we can now tell when the user has scrolled our target item into view, and we are ready to play sound. Last up, we need to create the rapid scrolling jump effect!&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto scrolling the user around
&lt;/h3&gt;

&lt;p&gt;In order for this jump scare to work properly, we want to rapidly scroll the user through the comic frames while playing a sound. The scariest part of this effect comes from the fact that the user thinks they have control of the pace of the comic, and we want to rapidly tear that control away from them.&lt;/p&gt;

&lt;p&gt;The way I built this was by scrolling to each part of the page I wanted to scroll the user to, and in the dev tools console, typing &lt;code&gt;window.scrollY&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn05lw7hexlrtc5ytfwar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn05lw7hexlrtc5ytfwar.png" alt="Screen Shot 2020-10-31 at 2.31.09 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that I had the Y coordinate I wanted, I just trigger the scroll effect at setTimeout intervals on each Y coordinate. The code looks like this:&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;playFirstJumpScare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;clickSound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollTo&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;8441&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollTo&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;9090&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollTo&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;9660&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&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;Future improvements could involve putting all the times and scroll positions into an array of objects and mapping through them:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scrollPos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8441&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;But today is Halloween already and I needed to release this thing, stat! So for now, the code stays ugly.&lt;/p&gt;

&lt;p&gt;Once I activate the jump scare, I set a boolean to true so that it doesn't happen again:&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;firstJumpScareActivated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Finally, to get the website online, I created a new GitHub account and used &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;, you can see the full code of the website &lt;a href="https://github.com/thatspookycomic/thatspookycomic.github.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this was informative, interesting, and most importantly, terrifying. Feel free to share the comic with all your friends this spooky last night of October! Happy Halloween!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>halloween</category>
    </item>
    <item>
      <title>How to create a scheduler with with Electron, Vue and node-schedule</title>
      <dc:creator>Nick Gottschlich</dc:creator>
      <pubDate>Wed, 12 Aug 2020 00:43:58 +0000</pubDate>
      <link>https://dev.to/nickgottschlich/how-to-create-a-scheduler-with-with-electron-vue-and-node-schedule-33ib</link>
      <guid>https://dev.to/nickgottschlich/how-to-create-a-scheduler-with-with-electron-vue-and-node-schedule-33ib</guid>
      <description>&lt;p&gt;Hello, my name is &lt;a href="https://twitter.com/NickGottschlich" rel="noopener noreferrer"&gt;Nick Gottschlich&lt;/a&gt;, and I am the creator of &lt;a href="https://github.com/Nick-Gottschlich/Social-Amnesia" rel="noopener noreferrer"&gt;Social Amnesia&lt;/a&gt;, an Electron/Vue application to help you delete your reddit and twitter content. One feature of this application is scheduling daily runs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkc1znkk12pcq22g6lvsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkc1znkk12pcq22g6lvsb.png" alt="Social Amnesia daily schedule feature to remove twitter content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested in how this was created, and how to create your own scheduling tool with Electron and Vue (think for an alarm clock, a daily reminder, a message scheduler, etc.), then read on!&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;If you haven't already set up your own Electron/Vue app, there are a few short steps to do that, using the vue CLI (command line interface).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -g @vue/cli&lt;/code&gt;&lt;br&gt;
&lt;code&gt;vue create electron-vue-node-scheduler&lt;/code&gt;&lt;br&gt;
&lt;em&gt;I like to use class-style component syntax, so to follow along with my code, select "typescript" and "Use class-style component syntax"&lt;/em&gt;&lt;br&gt;
&lt;code&gt;cd electron-vue-node-scheduler&lt;/code&gt;&lt;br&gt;
&lt;code&gt;vue add electron-builder&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, let's build prototype a simple component that will act as our user interface. We can use &lt;a href="https://bootstrap-vue.org/" rel="noopener noreferrer"&gt;Vue Bootstrap&lt;/a&gt; to speed up development.&lt;/p&gt;

&lt;p&gt;Install with &lt;code&gt;vue add bootstrap-vue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, we can start up our app with &lt;code&gt;yarn electron:serve&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Alright, now we can build the actual component.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;You new boilerplated Electron/Vue app comes with a convenient HelloWorld.Vue component which we will be modifying to be our scheduler component.&lt;/p&gt;

&lt;p&gt;Go ahead and copy paste this code over everything that was in HelloWorld.Vue:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;
      &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/b-time&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;}&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;vue-property-decorator&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="nd"&gt;Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scoped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="nx"&gt;CSS&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="nx"&gt;only&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="nx"&gt;scoped&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The b-time here is the &lt;a href="https://bootstrap-vue.org/docs/components/time#time" rel="noopener noreferrer"&gt;bootstrap-vue time component&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We now have set up an easy to use and good looking time selector component. Go ahead and play around with it:&lt;/p&gt;

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

&lt;p&gt;Great, now let's get the actual time out of this so we can use it for our scheduler.&lt;/p&gt;

&lt;p&gt;Here's how you will update your class component:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&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;onContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;context value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;data&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are keeping track of the value using &lt;a href="https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function" rel="noopener noreferrer"&gt;Vue's data function&lt;/a&gt; and we will get an update through &lt;code&gt;onContext&lt;/code&gt; whenever the time picker is changed by the user:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwrvw91bitcdl5aevjwbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwrvw91bitcdl5aevjwbo.png" alt="Time picker changed by user, console.log updated to show value in string format '19:12:00'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, now we can set up our scheduler. We will use &lt;a href="https://github.com/node-schedule/node-schedule#readme" rel="noopener noreferrer"&gt;node-schedule&lt;/a&gt; for this.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add node-schedule&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then in your  add a &amp;lt;code&amp;gt;import schedule from &amp;amp;quot;node-schedule&amp;amp;quot;;&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Then update your component like so:&amp;lt;br&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;div class="highlight"&amp;gt;&amp;lt;pre class="highlight javascript"&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class="k"&amp;gt;export&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;default&amp;lt;/span&amp;gt; &amp;lt;span class="kd"&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span class="nc"&amp;gt;HelloWorld&amp;lt;/span&amp;gt; &amp;lt;span class="kd"&amp;gt;extends&amp;lt;/span&amp;gt; &amp;lt;span class="nc"&amp;gt;Vue&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
  &amp;lt;span class="nx"&amp;gt;value&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="dl"&amp;gt;''&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;;&amp;lt;/span&amp;gt;

  &amp;lt;span class="c1"&amp;gt;// eslint-disable-next-line @typescript-eslint/no-empty-function&amp;lt;/span&amp;gt;
  &amp;lt;span class="nx"&amp;gt;scheduleJob&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;schedule&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nf"&amp;gt;scheduleJob&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;* * * * *&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{});&amp;lt;/span&amp;gt;

  &amp;lt;span class="nf"&amp;gt;onContext&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;context&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="c1"&amp;gt;// get hours and minutes off of something like '19:12:00'&amp;lt;/span&amp;gt;
    &amp;lt;span class="kd"&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;hours&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;context&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nf"&amp;gt;split&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)[&amp;lt;/span&amp;gt;&amp;lt;span class="mi"&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;];&amp;lt;/span&amp;gt;
    &amp;lt;span class="kd"&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;minutes&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;context&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nf"&amp;gt;split&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)[&amp;lt;/span&amp;gt;&amp;lt;span class="mi"&amp;gt;1&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;];&amp;lt;/span&amp;gt;

    &amp;lt;span class="k"&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;scheduleJob&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nf"&amp;gt;cancel&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;();&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;scheduleJob&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;schedule&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nf"&amp;gt;scheduleJob&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;
      &amp;lt;span class="s2"&amp;gt;`&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;minutes&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;||&amp;lt;/span&amp;gt; &amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class="s2"&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;hours&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;||&amp;lt;/span&amp;gt; &amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class="s2"&amp;gt; * * *`&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt;
      &amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
        &amp;lt;span class="nx"&amp;gt;console&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nf"&amp;gt;log&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;job ran on schedule!&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;);&amp;lt;/span&amp;gt;
      &amp;lt;span class="p"&amp;gt;},&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;);&amp;lt;/span&amp;gt;
  &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

  &amp;lt;span class="nf"&amp;gt;data&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;};&amp;lt;/span&amp;gt;
  &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Okay, this is getting a bit more confusing. Let&amp;amp;#39;s break it apart:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;code&amp;gt;scheduleJob = schedule.scheduleJob(&amp;amp;#39;* * * * *&amp;amp;#39;, () =&amp;amp;gt; {});&amp;lt;/code&amp;gt; This creates a local scheduleJob we can reference. We need to keep track of this, because any time the time is updated, we are going to cancel any previously scheduled jobs and create a new scheduled job.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;If the asterisks are confusing you, that&amp;amp;#39;s &amp;lt;a href="https://github.com/node-schedule/node-schedule#cron-style-scheduling"&amp;gt;cron-style scheduling&amp;lt;/a&amp;gt;:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img src="https://dev-to-uploads.s3.amazonaws.com/i/xugukl05tmyvv11oofl6.png" alt="cron style scheduling"/&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Now, onContext (meaning whenever the user changes the time schedule component), we will get a string in the format of &amp;amp;#39;HH:MM:SS&amp;amp;#39;. We want to break up this string to get hours, and minutes, which we do with the split function: &amp;lt;code&amp;gt;const hours = context.value.split(&amp;amp;#39;:&amp;amp;#39;)[0];&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Finally, we &amp;lt;code&amp;gt;cancel&amp;lt;/code&amp;gt; the previous scheduled job, and create a new one at the time the user has now decided:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img src="https://dev-to-uploads.s3.amazonaws.com/i/60yngargkfuieadba7z1.png" alt="screenshot showing job ran on schedule"/&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Great, that&amp;amp;#39;s it! You now have everything you need to build your own scheduler app! And how cool is this, since it&amp;amp;#39;s in electron, it&amp;amp;#39;s a native app that will run on Windows, Mac and Linux!&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Next I would recommend looking into Electron&amp;amp;#39;s &amp;lt;a href="https://www.electronjs.org/docs/api/tray"&amp;gt;Tray functionality&amp;lt;/a&amp;gt; so that you can minimize the app to the tray instead of closing it, so your scheduled job will still run at the right time while the app is running in the background.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here is the &amp;lt;a href="https://github.com/Nick-Gottschlich/electron-vue-node-scheduler"&amp;gt;GitHub Repo&amp;lt;/a&amp;gt; that you can look at if you want to see the code I used in this post.&amp;lt;/p&amp;gt;
&lt;/p&gt;

</description>
      <category>electron</category>
      <category>vue</category>
      <category>node</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
