<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Stuart Jones]]></title><description><![CDATA[Designs & Code]]></description><link>https://stuart-jones.com/</link><image><url>https://stuart-jones.com/favicon.png</url><title>Stuart Jones</title><link>https://stuart-jones.com/</link></image><generator>Ghost 3.13</generator><lastBuildDate>Thu, 23 Apr 2026 19:11:48 GMT</lastBuildDate><atom:link href="https://stuart-jones.com/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Sous Chef, The Cooking Stream Companion App]]></title><description><![CDATA[Sous Chef is a comprehensive companion application for hosting cooking shows on the Twitch livestreaming platform.]]></description><link>https://stuart-jones.com/blog/sous-chef-the-cooking-stream-companion-app/</link><guid isPermaLink="false">601b28eeac48ec069c96ddda</guid><category><![CDATA[Design]]></category><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 22 May 2021 19:24:36 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-03-06-at-3.28.11-PM-1.png" medium="image"/><content:encoded><![CDATA[<h2 id="overview">Overview</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/03/Screen-Shot-2021-02-28-at-4.34.24-PM-2.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-03-06-at-3.28.11-PM-1.png" alt="Sous Chef, The Cooking Stream Companion App"><p>Sous Chef is a comprehensive companion app for hosting cooking shows on the Twitch livestreaming platform. It integrates with every aspect of the streaming experience, providing animated video overlays for rendering show graphics, a web-based control panel for switching cameras and managing other show elements, and integration with Twitch's chat functionality for engaging with audiences.</p><p>The application is built upon a variety of open source tools, including Electron, Express, Socket.io, React, Webpack, and Bootstrap. Sous Chef has a modular architecture, allowing pieces to be added or removed depending on the host's particular needs. It was developed in collaboration with <a href="http://twitter.com/andrewcookslive">@AndrewCooksLive</a>, and its code can be found on <a href="https://github.com/imstubtw/sous-chef">GitHub</a>. Icons and graphics were provided by the <a href="https://mutant.tech/">Mutant Standard</a> alternative emoji set.</p><!--kg-card-begin: html-->
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="features">Features</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/03/Screen-Shot-2021-03-06-at-3.28.11-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><!--kg-card-begin: markdown--><ul>
<li>Runs locally as a native application on both macOS and Windows.</li>
<li>Provides a web-based control panel that can be accessed from any computer, tablet, or smartphone on the host computer's LAN.</li>
<li>Includes integration with <a href="https://github.com/Palakis/obs-websocket">OBS-Websocket</a> plugin for software-based scene switching and device muting.</li>
<li>Locally hosts a web overlay which can be integrated into the Open Broadcaster Software (OBS) or Streamlabs OBS. Overlay elements include:
<ul>
<li>Video inlay panels</li>
<li>Stream info panels (Recipe details, etc.)</li>
<li>Multiple simultaneous timers</li>
<li>Image and Tweet embeds (including animated gifs and Twitter videos.)</li>
<li>Probe thermometer temperature charts and live readouts</li>
<li>iTunes integration for displaying background music details.</li>
<li>Full-screen confetti and other celebratory animations</li>
</ul>
</li>
<li>Supports Twitch's chat API. Can post messages to chat, respond to viewer commands, allow select viewers to trigger overlay actions, and engage viewers with automated trivia questions.</li>
<li>Additionally supports Twitch's event APIs, allowing actions to automatically trigger when users perform standard actions such as following or subscribing.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="background">Background</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-09-at-6.31.12-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>This project might take a bit more explaining than usual. I never saw myself as the type of person to co-host a cooking show, but 2020 turned out to be an interesting time for everyone. By now most people are at least passingly familiar with Amazon's livestreaming video platform Twitch. It originally launched as a platform solely dedicated to allowing gamers to livestream their video gaming sessions, but in the years since it launched, they've opened it up to all sorts of non-gaming topics.</p><p>Twitch streams now feature everything from artists painting to live music concerts, or in our case, a homegrown cooking show. Hosting a show on Twitch is a bit like running you own TV show, with a few subtile differences. Most notably, there's a chatroom running alongside every broadcast. Your typical Twitch streamer isn't broadcasting out into the void, but rather interacting with a community of regular viewers. For our stream in particular, <a href="https://twitter.com/andrewcookslive">@AndrewCooksLive</a> handles the bulk of the culinary workload, and I help produce things. (I've been known to occasionally hop in and chop some onions when things get hectic.) It's been a fun way of staying connected to other people during these quarantine times, and stopped us from falling into a culinary rut.</p><p>In addition to having to come up with something new to cook every week, it turns out there is a <em>ton</em> of specialized technology that goes into producing a livestream. Your basic video game stream simply needs a webcam, a microphone, and streaming screen capture software. Your basic cooking stream however typically involves two or three cameras, wireless microphones, streaming software, and usually some additional plugins or programs. That's where Sous Chef comes in. What originally started as a side project to show recipe info and egg timers on our stream has morphed into the glue holding the entire show together.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="our-setup">Our Setup</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-02-at-4.01.12-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"><figcaption>Can't forget the dog cam!</figcaption></figure><p>While the software's important, I want to touch briefly on the hardware we're working with. Our setup and configurations could be its own specific writeup, but in brief, we're currently using:</p><ul><li>Three Logitech C920 Webcams</li><li>A wireless IP camera</li><li>Two wireless UHF lavalier microphones</li><li>A PC with enough horsepower to handle all of the above</li><li>A kitchen's worth of pots, pans, and accoutrement</li></ul><p>Everything's arranged in the kitchen through a variety of camera arms, tripods, and 3D printed mounts. (It's seriously tricky to hang a camera over a cutting board while still being able to chop efficiently.) Our next big project is to improve the lighting of the show, but for now, it's a pretty solid cooking stream setup for only a few hundred dollars. It also helps that we're currently not entertaining company who might have more than a few things to say about the number of USB cables scattered about our kitchen.</p><p>On the software side of things, you've got one big choice you need to make: Are you going to go with the Open Broadcaster Software's <a href="https://obsproject.com">OBS Studio</a>, or Streamlabs.com's custom <a href="https://streamlabs.com/streamlabs-obs">Streamlabs OBS</a>? OBS Studio is the classic piece of open source software that helped jumpstart the particular streaming revolution that we're currently in, while Streamlabs OBS is a proprietary layer of niceties on top of the open source software to make things run a bit more smoother. For the purposes of Sous Chef, either work. You simply need to be able to create a web browser overlay element, which both support.</p><p>To touch on Streamlabs.com for a moment: Their site touts it as the "All-in-One Live Streaming Software". If this sounds familiar, it's because Sous Chef is essentially a competitor to Streamlabs. Streamlabs can handle onscreen elements, manage chatbots, and even take care of complicated processes like taking money for donation drives. It's essentially the Wordpress of web streaming, if you have any experience in the blog CMS world. Streamlabs.com is the Swiss army knife of live streaming services, while Sous Chef is designed to be more specifically focused on the elements of live streaming that would be useful during a cooking stream. I generally prefer working with OBS Studio to avoid Streamlabs' constant nudging that I pay for a premium subscription, or check out their gallery of paid themes, but I still will occasionally use some of Streamlabs.com's different plugins for non-standard parts of our show. Either way, whichever flavor of OBS you decide to go with, Sous Chef should have something to offer.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h2 id="getting-started-with-sous-chef">Getting Started With Sous Chef</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-12.04.07-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Sous Chef is structured a bit unusually as far as <a href="https://www.electronjs.org/">Electron</a> applications go. Electron is a NodeJS framework which allows you to create cross-platform desktop applications using standard web technologies like HTML, CSS, and Javascript. In its most simple form, Electron creates a desktop app window, and then wants you to point it at an HTML file, a localhost URL, or an external web URL.</p><p>If you'd like to do something a bit more complicated than serving up a simple index.html file, there are two different approaches. The first is to use the popular <a href="https://create-react-app.dev">Create React App</a> boilerplate project to develop a complex React frontend for your Electron application. The second approach is to create a localhost environment using the <a href="https://expressjs.com/">Express</a> framework to create a robust backend that's also capable of serving the Electron frontend.</p><p>Sous Chef takes advantage of <em>both</em> of these approaches. Express manages a backend API, and serves the Sous Chef control panel to any browser on the local network. Create React App handles the inner workings of the control panel itself, and brings along some niceties for developing a complex React application like module hot-reloading. When working in dev mode, Create React App will host the frontend on its own localhost port using webpack, and Electron and Express will both just reference this instance. When compiling the application, Create React App will compile the application down to an index.html file that the Express instance manages serving.</p><p>So at a high level, the application structure looks something like this:</p><!--kg-card-begin: markdown--><ul>
<li>sous-chef/ (Top-level app folder)
<ul>
<li>plugins/ (Express back-end modules)
<ul>
<li>recipe.js</li>
<li>timer.js</li>
<li>...</li>
</ul>
</li>
<li>public/ (Create React App flat files)
<ul>
<li>favicon.ico</li>
<li>index.html</li>
<li>...</li>
</ul>
</li>
<li>src/ (Create React App React modules)
<ul>
<li>index.css</li>
<li>index.js</li>
<li>Control/
<ul>
<li>Control.js</li>
</ul>
</li>
<li>Overlay/
<ul>
<li>Overlay.js</li>
</ul>
</li>
<li>...</li>
</ul>
</li>
<li>main.js (App entry point, manages Electron &amp; Express configs)</li>
<li>package.json (App configs)</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><p>Go ahead and run <code>npm install</code>. While npm fetches all the required packages, go ahead and take a look at the package.json file's script section. At the risk of losing you to another bulleted list, you can see the various commands that will finally allow us to finally get this bad boy up and running.</p><!--kg-card-begin: markdown--><ul>
<li>&quot;start&quot;: &quot;cross-env PORT=3000 react-scripts start&quot;
<ul>
<li>Launch the application's Create React App</li>
</ul>
</li>
<li>&quot;electron&quot;: &quot;wait-on tcp:3000 &amp;&amp; electron-forge start&quot;
<ul>
<li>Launch the application's Electron main.js component only after it sees a service running on port 3000.</li>
</ul>
</li>
<li>&quot;dev&quot;: &quot;concurrently -k &quot;BROWSER=none npm start&quot; &quot;npm:electron&quot;&quot;
<ul>
<li>Use concurrently to launch the <code>start</code> and <code>electron</code> scripts at the same time. Supress Create React App's standard browser-opening behavior.</li>
</ul>
</li>
<li>&quot;build&quot;: &quot;cross-env PORT=80 react-scripts build&quot;
<ul>
<li>Build the Create React App instance using port 80.</li>
</ul>
</li>
<li>&quot;make&quot;: &quot;cross-env PORT=80 react-scripts build &amp;&amp; electron-forge make&quot;
<ul>
<li>Build the Electron application using port 80.</li>
</ul>
</li>
<li>&quot;package&quot;: &quot;cross-env PORT=80 react-scripts build &amp;&amp; electron-forge package&quot;
<ul>
<li>Build the Create React App instance using port 80, then make and package the Electron application for distribution.</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><p>As you can see, these scripts typically build on top of each other. <code>npm run dev</code> launches <code>npm start</code> and <code>npm electron</code> together, while <code>npm run package</code> kicks off <code>npm run build</code> and <code>electron-forge package</code> at the same time. Along with the packages I've already discussed, you've probably noticed that Sous Chef takes advantage of the <a href="https://www.electronforge.io">Electron Forge</a> toolkit to streamline the build and distribution process.</p><p>You're <em>almost</em> ready to get started. There's just one last piece that needs taking care of. Sous Chef uses the <a href="https://dev.twitch.tv/">Twitch</a> and <a href="https://developer.twitter.com/">Twitter</a>  APIs for fetching data and interacting with 3rd party services. You'll need developer API keys for both websites, so head over to their developer sections, register, and create a new dev application. Once you've got Twitch's client ID, go ahead and copy the format in <code>.twitch-keys_example.json</code> to create <code>.twitch-keys.json</code>. Paste your client ID into the new file's json. Do the same for <code>.twitter-key_example.json</code>. You'll need to put your Twitter consumer key, consumer secret, and bearer token into <code>.twitter-key.json</code>. Twitter can take a few days to create a dev account for new users, so go ahead and do this process now if you're thinking about using Sous Chef.</p><p>Alright, that's everything! Go ahead and fire up <code>npm run dev</code>, and you should see Sous Chef start. If everything looks good, you can use <code>npm run package</code> to create a nice compiled application with a single icon to click.</p><h2 id="using-sous-chef">Using Sous Chef</h2><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-03-06-at-3.28.11-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><h3 id="basic-setup">Basic Setup</h3><p>Once you've got OBS up and running, you'll need to integrate its overlay into your OBS scenes. Create a browser overlay element, and make sure it's the same resolution and size as your screen. Set the source to Sous Chef's overlay URL. There's a few options for this, which can be found in the Config tab of Sous Chef. There's the human-friendly <code>http://souschef.local/overlay</code> for system's with Apple's Bonjour multicast DNS service installed, <code>http://[Your IP]/overlay</code> as a fallback for systems without Bonjour, and <code>http://localhost/overlay</code> if you don't need to access Sous Chef on your LAN. If Sous Chef is running in dev mode, make sure to put <code>:3000</code> after the domain.</p><p>If you'd like to create a scene without the video inset panel, add the query string parameter <code>?noInset=true</code> to the end of the URL.</p><p>If you haven't already, go install the <a href="https://github.com/Palakis/obs-websocket">OBS-Websocket</a> plugin. This will allow Sous Chef to remotely control OBS from outside of the OBS application. You may keep OBS-Websocket's settings at their defaults. If you need to change the default port or would like to add authentication, these settings are managed in /plugins/obs.js via the obs-websocket-js package.</p><p>The "Now Playing" iTunes integration does not require any specific configuration. If you're using iTunes on Windows, iTunes on the Mac, or Apple's new Music app on the Mac, Sous Chef will automatically show song information and cover art when a new track starts playing.</p><p>The Sous Chef control panel is divided into three main sections: Cook, Prep, and Config. </p><h3 id="cook-tab">Cook Tab</h3><!--kg-card-begin: markdown--><h4 id="sceneswitchinghotkeys">Scene Switching &amp; Hotkeys</h4>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.57.34-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The top of Sous Chef has your classic scene-switcher interface. The button layout is designed to mimic <a href="https://www.partsnotincluded.com/diy-stream-deck-mini-macro-keyboard/">Dave Madison DIY Streamdeck project</a>. We've got one at the base of the stove, so it's nice to have the same button layout mapped in software as well. It provides a quick interface to move between the stove, cutting board, and kitchen cameras, as well as quick actions such as muting the microphones or toggling confetti.</p><h4 id="active-timer">Active Timer</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.57.25-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Active Timer panel lists the timers currently being displayed on the overlay. Once a timer expires, the control panel will play a whistle sound effect, and the expired time will begin to flash red. There will also be an attention-grabbing red flashing boarder on the overlay itself. Timers can be canceled or restarted at any time. The scene Switching and Active Timers panels are responsive, so they'll be displayed side-by-side if the control panel is large enough.</p><h4 id="new-timer">New Timer</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.50.10-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The New Timer panel provides an interface for creating new timers. Users can optionally give the timer a unique name, and either provide a specific amount of minutes and seconds, or use one of the provided hotkeys for common timer amounts.</p><h4 id="show-image">Show Image</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.50.19-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Sometimes a a picture is worth a thousand words, and you'd just like to show your viewers a specific image from somewhere on the web. The Show Image panel allows you to create a pop-up image panel on the overlay software. It accepts an image URL, and provides options for rotating images in the case that OBS's browser element has difficulty reading a camera's rotation EXIF data. The control panel provides a small thumbnail of the currently displayed image so that the producer can have immediate feedback as to whether or not anything is currently being displayed on the overlay.</p><h4 id="show-tweet">Show Tweet</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.50.52-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Similar to the Show Image panel, the Show Tweet panel allows you to embed a tweet from Twitter.com on the overlay. The embed panel supports Twitter video and Twitter's .gifv videos, allowing you to quickly show off some animated content.  </p><h4 id="probe-thermometer-control">Probe Thermometer Control</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.49.13-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>This section could honestly be a standalone blog post. After a few streams involving frying, we realized that there's not much for the audience to watch while the oil warms up. There's a number of fry recipes that rely on maintaining a particular fry oil temperature, so we decided why not feed two birds with one scone and display our probe thermometer's output on the overlay itself?</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Nov-24-2020_23-27-48-3.gif" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>This is of course easier said than done. We'd been using the reliable <a href="https://www.thermoworks.com/ChefAlarm">Thermoworks Chef Alarm</a>, but it doesn't provide any Bluetooth or Wifi connectivity that we could piggyback off of for the overlay. Rather then investing in a new thermometer, I decided to build a small probe-thermometer-to-PC adapter using an Arduino-based microcontroller. The Arduino reads the electrical resistance of the probe thermometer, passes it through the <a href="https://github.com/giannivh/SmoothThermistor">Steinhart–Hart equation</a> to determine how hot the probe is, and then sends the data to the PC with a basic USB serial signal. There's even a small seven-segment display built in for some immediate feedback.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Nov-24-2020_23-09-21-2.gif" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Probe Control panel in Sous Chef lets you control what you'd like to do with this thermometer data. Once you selected the proper serial port, you can either display the current temperature, or you can show a live-updating plot of the data with a Chart.js graph. There's also the ability to set an alarm to go off when a target temperature is reached.</p><h4 id="toggles">Toggles</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.00-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Alright, this one's a bit more straightforward. There's a few less-frequently used overlay elements such as confetti or an announcement banner. This panel does what it says on the tin, and allows you to toggle these on and off.</p><h4 id="remote-producers">Remote Producers</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.08-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>Part of streaming is letting your audience get in on the fun. A few of the overlay's elements have been tied to chat commands like <code>!cuttingboard</code> or <code>!confetti</code>. The Remote Producers panel allows you to grant permissions to specific users to take over producing responsibilities for the stream.</p><h3 id="prep-tab">Prep Tab</h3><h4 id="recipe-drink">Recipe &amp; Drink</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.16-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Recipe and Drink panels control a small overlay element which let people know what's on the menu for the evening, and what cooking beer you're enjoying during the stream. The URL section controls a small chat bot which responds to !recipe or !drink.</p><h4 id="reminders-bot">Reminders Bot</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.25-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The Reminders Bot is a Twitch chatbot which posts specific messages at a regular interval. This is usually used to remind people of ongoing charity drives and other events.</p><h4 id="trivia-bot">Trivia Bot</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.51.51-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>We've been experimenting with a Trivia Bot recently as a way to boost viewer engagement. It's currently managed through a small trivia.json file stored within Sous Chef itself. The trivia bot will post a message at a regular interval, allowing people to score points for various stream prizes.</p><h3 id="config-tab">Config Tab</h3><h4 id="twitch-integration">Twitch Integration</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.52.00-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The aforementioned Twitch integration is controlled through Twitch's OAuth API. Sous Chef requires two separate logins. Collecting stream event data requires the broadcaster to log in, while posting chat bot messages requires the specific chat bot account to login.</p><h4 id="obs-integration">OBS Integration</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.52.05-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The camera switching components rely on OBS's websocket plugin. The connection kicks in automatically when Sous Chef launches, but its status can be double-checked here if something's gone wrong.</p><h4 id="sous-chef-information">Sous Chef Information</h4><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2021/05/Screen-Shot-2021-05-22-at-1.52.12-PM.png" class="kg-image" alt="Sous Chef, The Cooking Stream Companion App"></figure><p>The application has two main web endpoints: The control panel interface, and the overlay itself. The Sous Chef Information panel provides links to these pages in a variety of forms for testing purposes. The most user-friendly of these links is the souschef.local Bonjour networking (mDNS) URL. If the client machine does not have Bonjour installed however, a direct IP option may be used as well.</p><h1 id="closing-thoughts">Closing Thoughts</h1><p>Sous Chef has been a fun quarantine project. It's helped me learn a lot about NodeJs, React, and integrating with 3rd party services and clients. The app is currently fine-tuned to our cooking stream's specific needs, but if it looks like something you would be interested in using, don't hesitate to reach out to me with ideas. All of the code is up on my <a href="https://github.com/imstubtw/sous-chef">Github</a> for forking as well.</p></div>]]></content:encoded></item><item><title><![CDATA[Give Back Case Study]]></title><description><![CDATA[Give Back is a case study for a community volunteering and charitable giving platform design to allow corporate team members to more easily connect with causes in their local area.]]></description><link>https://stuart-jones.com/blog/give-back-case-study/</link><guid isPermaLink="false">5e94b273017a114be0c9615e</guid><category><![CDATA[Projects]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Mon, 13 Apr 2020 19:41:23 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Cycling-Enthusiast-Persona-2-2.png" medium="image"/><content:encoded><![CDATA[<h1 id="summary">Summary</h1><img src="https://stuart-jones.com/content/images/2020/04/Cycling-Enthusiast-Persona-2-2.png" alt="Give Back Case Study"><p>Give Back is a community volunteering and charitable giving platform design to allow corporate team members to more easily connect with causes in their local area. It uses personalized results to proactively recommend events to local team members.</p><p>This product design was conducted as part of the Austin Coding Academy's Product Design Academy program. It served as the overall capstone of the course. For more details on the final design, as well as the Adobe XD file, please view this accompanying blog post.</p><!--kg-card-begin: html-->
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="problem-statement">Problem Statement</h1><p>Team members do not know what community volunteering and charitable giving opportunities are going on in their local areas.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="research-data">Research Data</h1><p>Employees who volunteer in their local community consistently score higher on all measures of job satisfaction than those who don't. Creating a strong culture of volunteerism not only improves the company's public image, but it strongly contributes to talent retention.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Capture-3.PNG" class="kg-image" alt="Give Back Case Study"></figure><p>Currently 52% of team members of team members engage in volunteering events. The company's 2030 social impact goal is to increase this metric to 75%. I believe with this new platform's personalized recommendations, 60% engagement would be an engaging goal to strive for.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Capture2.PNG" class="kg-image" alt="Give Back Case Study"></figure><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Capture-2-2.PNG" class="kg-image" alt="Give Back Case Study"></figure><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="customer-research-survey">Customer Research Survey</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Survey.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Customer Research Survey</figcaption></figure><p>A brief <a href="https://forms.gle/zbJZP3oFSK91t1gN9">customer research survey</a> was created using Google Forms. It features branching logic to gain further insights based off of whether or not the respondent currently volunteers, or if they did not participate in volunteering events in 2019.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="user-interview-questions">User Interview Questions</h1><p>Additionally, a set of user interview questions were created for a moderated guided discussion. The initial hypothesis of the interviews was that users were having trouble discovering new volunteering opportunities.</p><ol><li>Have you volunteered in the past?</li><li>What sort of volunteer activities do you enjoy?</li><li>What motivates you to volunteer?</li><li>How do you hear about new volunteer opportunities?</li><li>Do these opportunities generally align with your interests?</li><li>Are you satisfied with the amount of time you're donating?</li><li>Who do you typically volunteer with?</li><li>What do you think about our current volunteering tool?</li><li>Do you ever donate to charities online?</li><li>Do you use Dell's charity portal to make matching donations?</li></ol><p>At the end of the interviews, the hypothesis was proven correct. I proceeded to conduct several additional exercises to better understand the potential applicaton's users.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="personas">Personas</h1><p>Three personas were created based off of my interviews. They briefly summarize the different attitudes I encountered when it came to work-sponsored volunteering.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Cycling-Enthusiast-Persona-2-1.png" class="kg-image" alt="Give Back Case Study"><figcaption>Cycling Enthusiast Persona</figcaption></figure><p>The cycling enthusiast persona was key to developing the event-driven features of Give Back. They participate in several different charity bike rides throughout the year, but volunteer in a largely cause-independent fashion.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Cause-Focused-Persona-1.png" class="kg-image" alt="Give Back Case Study"><figcaption>Cause-Focused Volunteer</figcaption></figure><p>Conversely, the cause-focused volunteer is centered around a breast cancer survivor. Due to their personal connection with the cause, they will generally drop everything to participate in a breast cancer research or support related volunteer activity.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Low-Interest-Volunteer-Persona-1.png" class="kg-image" alt="Give Back Case Study"><figcaption>Low-Interest Volunteer</figcaption></figure><p>Finally, the low interest volunteer represented users who generally just volunteered during team-sponsored events. They were a useful case to study, because even though they rarely volunteered, they could name multiple charities that they felt strongly about and supported.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="empathy-map">Empathy Map</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Empathy-Map-1-2.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Empathy Map for Cycling Enthusiast</figcaption></figure><p>In addition to the persona exercise, an empathy map was also created for the cycling enthusiast. This alternative format to the persona card was useful for capturing the thoughts and feelings of the users I interviewed.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="journey-maps">Journey Maps</h1><p>As the last step of the user research, journey maps were created for several use cases. These design artifacts were useful when envisioning how different functionality would work in the tool.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Map2.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Cycling Enthusiast Journey Map</figcaption></figure><p>The Cycling Enthusiast knows what volunteer events they like to participate in. They're a cyclist, and want to know about upcoming charity bike rides. This user is proactive enough to log into the site well before events and register their interest. They map go the additional step of enabling email communications to hear about upcoming charity rides.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Map3.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Cause-Focused Event Coordinator Journey Map</figcaption></figure><p>The Event Coordinator comes from the other direction. They're running a volunteer opportunity in town, and are looking to get the word out on their event. The more ways they can spread the word and engage their volunteering teams, the better.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Map1.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Low-Interest Volunteer Journey Map</figcaption></figure><p>The Late Planner is not significantly engaged with community volunteering. They typically join events when their team is going as a group, or when their organization is targeting a specific volunteering goal. They primarily interact with the site when they are looking for events happening in the near future, or geographically close to their home or office.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="mood-board">Mood Board</h1><p>Finally, the application design process was ready to begin. A digital mood board was created with various images from past volunteering events to create an impression of what Give Back was aiming to enable.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/image-4.png" class="kg-image" alt="Give Back Case Study"><figcaption>Give Back Digital Mood Board</figcaption></figure><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="key-performance-indicators">Key Performance Indicators</h1><p>In addition to the 2030 goal of getting 75% of the company's team members volunteering, Give Back is tracking the following KPIs:</p><ul><li>Number of volunteer activities performed.</li><li>Number of hours volunteered.</li><li>Number of charitable donations made.</li><li>Amount of charitable donations raised.</li><li>Number of employees with a personalized out user profile.</li><li>Decreased amount of time to perform common tool actions.</li></ul><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="sketches">Sketches</h1><p>I created several pen-and-paper sketches of key pages of the application. These sketches tested several different layouts, such as a list-based category page and a search-based category page.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/IMG_3358-2.jpg" class="kg-image" alt="Give Back Case Study"><figcaption>Two Home Page Sketches</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/IMG_3354-2.jpg" class="kg-image" alt="Give Back Case Study"><figcaption>Two Category Page Sketches</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/IMG_3355-2.jpg" class="kg-image" alt="Give Back Case Study"><figcaption>Two Event Detail Page Sketches</figcaption></figure><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="lo-fi-wireframes">Lo-Fi Wireframes</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Give-Back-Wireframe-Home.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Lo-Fi Wireframes</figcaption></figure><p>Once the general idea was sketched, I created a set of lo-fi wireframes using Adobe XD. These wireframes allowed me to test and prototype different interactions. If you would like a closer detail of my lo-fi wireframes, they're avalible on the <a href="https://xd.adobe.com/view/ac1189bd-9317-48ce-5d91-631199154a44-da20/">Adobe XD preview site</a>.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="design-system">Design System</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Dell-Design-System.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>The Dell Design System</figcaption></figure><p>Before the hi-fi wireframes could be created, I needed to determine which design system or UI framework I would be working with. As part of this product design exercise, my course was also giving us an opportunity to work with a real-world corporate design system. My Give Back design mockup uses the <a href="https://www.delldesignsystem.com/">Dell Design System</a> for it's branding guidelines and core components. I also created a few additional volunteer-centric components which did not exist as part of the Dell Design System.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="hi-fi-wireframes-prototype">Hi-Fi Wireframes &amp; Prototype</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Dell-Give-Back-Hifi-Screenshot-2-1.PNG" class="kg-image" alt="Give Back Case Study"><figcaption>Hi-Fi Wireframes</figcaption></figure><p>The final high fidelity wireframe for the Give Back project was once again created in Adobe XD. The tool made it quick and easy to test different page designs, as well as work with Dell Design System components. The high fidelity wireframes focus on five key pages of the application, demonstrating a straightforward user flow for joining an event. If you'd like to browse the full mockup, it is available on the <a href="https://xd.adobe.com/view/27b779c3-6630-4373-519a-8b50e62b56dd-5186/?fullscreen">Adobe XD preview website</a>. Additionally, I've created a dedicated <a href="https://stuart-jones.com/new/blog/give-back-design-mockup/">blog post</a> to discuss the final design of the site, and the way the different portions of the site are structured.</p><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="elevator-pitch">Elevator Pitch</h1><p>Now that the overall Give Back design had been created, I still needed to be able to successfully pitch the idea. The first component was a 30-second elevator pitch succinctly summarizing Give Back's value proposition and differentiating factors.</p><blockquote>Dell Give Back is a community engagement and charitable donation platform designed to engage Dell team members with their local area. It gives personalized recommendations based on the types of events users like to perform, the charities they like to support, and the causes they care the most about. This new platform not only supports its Legacy of Good goals, but it helps support Dell's people strategy as well. Employees who are engaged with their community score higher across every job satisfaction metric.</blockquote><!--kg-card-begin: html--></div>
<div class="og-main-interior"><!--kg-card-end: html--><h1 id="pitch-deck-presentation">Pitch Deck &amp; Presentation</h1><p>The last asset created for the Give Back product design was a basic <a href="https://xd.adobe.com/view/e6721b4b-3d6c-413f-54f0-d3710d2a6eba-9043/?fullscreen">pitch deck</a>. At the end of the course, we were each given a five minute time slot to present our work. I created my presentation within Adobe XD, so the interactive mockup could be embedded with the slides.</p></div>]]></content:encoded></item><item><title><![CDATA[Give Back Design Mockup]]></title><description><![CDATA[Give Back is a community engagement and charitable giving web site design mockup I completed in the Spring of 2020 as part of the Austin Coding Academy's Product Design Academy.]]></description><link>https://stuart-jones.com/blog/give-back-design-mockup/</link><guid isPermaLink="false">5e9378f8017a114be0c9614d</guid><category><![CDATA[Projects]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sun, 12 Apr 2020 20:24:43 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Dell-Give-Back-Hifi-Screenshot-copy-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://stuart-jones.com/content/images/2020/04/Dell-Give-Back-Hifi-Screenshot-2.PNG" class="kg-image" alt="Give Back Design Mockup"><figcaption>Give Back Homepage</figcaption></figure><img src="https://stuart-jones.com/content/images/2020/04/Dell-Give-Back-Hifi-Screenshot-copy-1.png" alt="Give Back Design Mockup"><p>Give Back is a community engagement and charitable giving web site design mockup I completed in the Spring of 2020 as part of the Austin Coding Academy's Product Design Academy. The design served as the capstone project for the course, and took the participants through the entire product design process. The full design can be viewed on the <a href="https://xd.adobe.com/view/27b779c3-6630-4373-519a-8b50e62b56dd-5186/?fullscreen">Adobe XD preview website</a>. Please see my related blog post for the full Give Back Case Study.</p><p>After meeting with users of the current volunteering solution, the common feedback we received is that users generally need to rely on communication channels outside of the existing application to discover new volunteer opportunities. The majority of volunteering events team members participated in were either heard about through word of mouth, organized by their team/organization, or communicated out through other social groups. The primary innovation Give Back is looking to bring to the volunteering application space is recommendations based on user's volunteering priorities.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/05/Hi-Fi.PNG" class="kg-image" alt="Give Back Design Mockup"></figure><p>This ended up taking the form of three categories: Event Type, Causes, and Charities.</p><p>Event type focuses on the way team members prefer to engage with their community. Some people like engaging directly with the people they're benefiting, others like working behind the scenes, and others still enjoy raising funds or awareness for a cause. Allowing users to prioritize their event type makes sure that cyclists hear about upcoming charity bike rides, etc.</p><p>The Cause category allows users to make sure their volunteering efforts contribute to the issues most important to them. From breast cancer research to feeding the poor, allowing users to receive personalized recommendations based off of cause ensures that new volunteers without any prior connections to existing organizations can still meaningfully contribute to the issues they care about.</p><p>Finally, the Charity category is for team members who already care deeply about a specific charity. Whether they've had a good volunteering experience with them in the past, or just believe strongly in their mission, making sure users can stay connected with the charities they care about can be a strong motivator to continue volunteering.</p><p>The design mockup itself was created using Adobe XD. In addition to being a product design exercise, this project was also an opportunity to gain experience working with a design system. Give Back was based upon the <a href="https://www.delldesignsystem.com/">Dell Design System</a>, incorporating existing brand elements and components.</p>]]></content:encoded></item><item><title><![CDATA[Internet Famous, An Open-Source Rendition of a Party Game Classic]]></title><description><![CDATA[Internet Famous is a digital version of the classic Celebrities or Hat Game party game. More specifically, it's an open-source version of Alex Hague and Justin Vickers' excellent Monikers card game.]]></description><link>https://stuart-jones.com/blog/internet-famous-an-open-source-rendition-of-a-party-game-classic/</link><guid isPermaLink="false">5e913bf4017a114be0c96063</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 18 Jul 2018 22:14:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Internet-Famous-2.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Internet-Famous-2-1.png" class="kg-image" alt="Internet Famous, An Open-Source Rendition of a Party Game Classic"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Internet-Famous-2.png" alt="Internet Famous, An Open-Source Rendition of a Party Game Classic"><p><a href="https://stuart-jones.com/internet-famous/">Internet Famous</a> is a digital version of the classic Celebrities or Hat Game party game. More specifically, it's an open-source version of Alex Hague and Justin Vickers' <a href="https://github.com/ImStuBTW/internet-famous/blob/master/Monikers">Monikers</a>. The game itself is pretty straightforward. You spit your group into two teams, and have 60 seconds to get your team to guess as many weird, quirky card titles as possible. In the first round, you can say anything you want to. In the second round, you can only say a single word. In the third round, you can't say a thing, and play charades to get your friends to guess the card. The catch is that reach round is played with the same deck of cards, meaning that there's a pretty good chance you'll come up with some good in-jokes by the time you make it to the last round.</p>
<h2 id="howtoplay">How to Play</h2>
<p>Skip the boring compiling and building stuff, just point your web browser at <a href="https://stuart-jones.com/internet-famous/">/internet-famous/</a></p>
<h2 id="howtobuild">How to Build</h2>
<p>Oh, you actually want to do the compiling and building stuff. Internet Famous is built on the React JavaScript framework. It uses Redux as a state container, and Express as a simple webhost. The app doesn't have any logins or persistent state, which means it's pretty simple to build. The package.json file contains all of the NPM packages you'll need, and webpack puts everything together. If you'd like to run the game locally in development mode, copy this repository and run npm start -s to get a local sever running. If you'd rather build the game for production, run npm run build, and upload the resulting Node app to your webserver of chioce.</p>
<h2 id="disclaimerslicensing">Disclaimers &amp; Licensing</h2>
<p>Emoji art assets provided by EmojiOne 2.3, a Creative Commons BY 4.0 project. React-friendly SVG library courtesy of wilhearts' <a href="https://www.npmjs.com/package/react-svg-emojione">https://www.npmjs.com/package/react-svg-emojione</a> NPM package.</p>
<p>Internet Famous is a Creative Commons BY-NC-SA 4.0 derivative of the card game Monikers. The name Monikers is a registered trademarked of Palm Court LLC. Internet Famous is unaffiliated with Monikers and Palm Court LLC. If you enjoy Internet Famous, why not pick up a physical copy of their game at <a href="http://www.monikersgame.com">http://www.monikersgame.com</a>?</p>
<p>This work is available under the MIT and Creative Commons BY-NC-SA 4.0 licenses.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[BitBot - A BitBucket Push Bot for Discord and Slack]]></title><description><![CDATA[BitBot is a small Node script I created for sending commit messages from BitBucket pushes to the Discord or Slack room of your choice. BitBot takes advantage of BitBucket's outgoing webhooks, and Discord/Slack's incoming webhooks.]]></description><link>https://stuart-jones.com/blog/bitbot-a-bitbucket-push-bot-for-discord-and-slack/</link><guid isPermaLink="false">5e913bf4017a114be0c96060</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Mon, 06 Nov 2017 16:52:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2018-01-03-at-10.57.23-AM-2-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2018-01-03-at-10.57.23-AM-2.png" class="kg-image" alt="BitBot - A BitBucket Push Bot for Discord and Slack"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2018-01-03-at-10.57.23-AM-2-1.png" alt="BitBot - A BitBucket Push Bot for Discord and Slack"><p>While Github is all the rage, BitBucket is still a popular alternative for code repositories. A gamedev buddy <a href="https://twitter.com/redvonix">@RedVonix</a> has been working on an upcoming title, and wanted to automate BitBucket commit notifications to his Discord chat. This is built into Discord and Slack if you're using GitHub, but Bitbucket took a little extra finagling. BitBot is a small Node script I created for sending commit messages from BitBucket pushes to the Discord or Slack room of your choice. BitBot takes advantage of BitBucket's outgoing webhooks, and Discord/Slack's incoming webhooks. You'll need your own server running NodeJS in order to receive and pass on the messages. The source can be found on my <a href="https://github.com/ImStuBTW/bitbot">GitHub page</a>.</p>
<h2 id="configurediscordwebhooks">Configure Discord Webhooks</h2>
<p>Navigate to the channel of the Discord server you would like BitBot to post messages in. Click the gear icon next to the channel name. (Note: You will need admin privileges on the server to configure webhooks and use BitBot.) In the channel settings window, click 'Webhooks' on the left hand side of the screen. Click 'Create Webhook'. Enter a bot name, confirm the channel, and optionally upload an icon. Copy the Webhook URL, and click save.</p>
<h2 id="configureslackwebhooks">Configure Slack Webhooks</h2>
<p>Navigate to the channel of the Slack server you would like BitBot to post messages in. Click the gear icon on the top of the page. Select 'Add an app' from the dropdown menu. Your Slack channel's app manager will appear in a new browser window. (Note: You will need admin privileges on the server to configure webhooks and use BitBot.) Click 'Manage' in the top right of the browser window. Select 'Custom Integrations' from the left side of the page. Choose 'Incoming Webhooks', and then 'Add Configuration'. Slack will ask you which channel you would like the bot to post into. Click 'Add Incoming Webhook Integration'. On the next page, you can configure your bot further. Copy the Webhook URL.</p>
<h2 id="configurebitbot">Configure BitBot</h2>
<p>Clone this repository to an empty public folder or subdomain on a server running Node. Edit the config.js file. Set the name to whatever you would like BitBot's display name in the chat to be. Choose a port, and paste in at least one Webhook URL from the previous steps. BitBot can post to both Discord and Slack, or just one of these services. If you are not using a service, just leave the single quotes blank.</p>
<h2 id="buildandrunbitbot">Build and Run BitBot</h2>
<p>Run an <code>npm init</code> command to download the required npm packages. Run <code>npm start</code> to begin running BitBot.</p>
<h2 id="configurebitbucket">Configure BitBucket</h2>
<p>Finally, browse to your BitBucket repository. Click the 'Settings' option and choose 'Webhooks' from the settings menu. Click 'Add webhook'. Give your webhook a title, and paste in the server URL where you installed BitBot. Make sure 'Active' is checked. Check the 'Skip certificate verification' checkbox if your server is not running SSL. Make sure 'Triggers' is set to 'Repository push'.</p>
<p>Congratulations, BitBot should now be up and running.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Undercast - A Mac & Windows Desktop App for Overcast with Media Key Support]]></title><description><![CDATA[Undercast is a small third-party desktop applet for Marco Arment's popular iOS podcatching app Overcast.]]></description><link>https://stuart-jones.com/blog/undercast-a-mac-windows-desktop-app-for-overcast-with-media-key-support/</link><guid isPermaLink="false">5e913bf4017a114be0c9605d</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 21 Dec 2016 21:33:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/screenshot-5-2-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/screenshot-5-2.png" class="kg-image" alt="Undercast - A Mac & Windows Desktop App for Overcast with Media Key Support"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/screenshot-5-2-1.png" alt="Undercast - A Mac & Windows Desktop App for Overcast with Media Key Support"><p>Undercast is a small third-party desktop applet for <a href="https://marco.org/">Marco Arment's</a> popular iOS podcatching app <a href="https://overcast.fm/">Overcast</a>. This project can also be found on <a href="https://github.com/ImStuBTW/undercast">Github</a>.</p>
<h1 id="whatsundercastfor">What's Undercast For?</h1>
<p>Overcast has become a popular alternative to Apple's first-party Podcasts app. It's simple and clean interface makes it easy to access your shows, and it has a few nice power features like voice boost and smart speed. Overcast also has a syncing service to manage your subscriptions and playback progression across devices, but it unfortunately lacks an official Mac or Windows client. Overcast <em>does</em> sync your data with <a href="https://overcast.fm/">Overcast.fm</a> however. <a href="https://overcast.fm/">Overcast.fm</a> lets you access your subscriptions, and offers a basic web-based player for listening to your shows.</p>
<p>On it's own, <a href="https://overcast.fm/">Overcast.fm</a> is a good solution for accessing your podcasts on the desktop when in a pinch. It lives in one of your browser tabs however, so there's a chance you can accidentally close it, and more critically, it lacks support for the ubiquitous keyboard media keys.</p>
<p>Undercast tries to make up for these shortcomings. Rather than writing an entirely new podcasting client that highjacks Overcast's syncing service, Undercast simply wraps the <a href="https://overcast.fm/">Overcast.fm</a> web player inside of a desktop applet that lives in your menubar or system tray.</p>
<h1 id="features">Features</h1>
<ul>
<li>Undercast lives in your menubar or system tray.
<ul>
<li>Click on it to pull up the Overcast window, or right click on it to access the options or quit.</li>
</ul>
</li>
<li>Media Key Support. Undercast supports keyboard media keys on both Mac and Windows.
<ul>
<li>Play keys will play and pause your podcast. Stop keys will pause the currently playing podcast. Forward/Rewind keys will jump your podcast forward or back by a few seconds.</li>
<li>The amount of seconds Undercast skips is determined by a setting on the iOS app. In Overcast, tap on the Overcast icon on the top left of the main screen, then go into the &quot;Nitpicky Details&quot; menu, then change &quot;Seek Back By&quot; and &quot;Seek Forward By&quot; to your desired values.</li>
<li>By default, Undercast will attempt to bind your system's global media keys to it. If you would like to free up control of your system's media keys to another application, right click on the icon and deselect &quot;Bind Media Keys&quot;.</li>
</ul>
</li>
<li>Playback Indicator
<ul>
<li>By default, Undercast's icon changes from white to orange when a podcast is playing. This setting can be disabled in the right click menu.</li>
</ul>
</li>
</ul>
<h1 id="howundercastworks">How Undercast Works</h1>
<p>Undercast uses a technology called <a href="http://electron.atom.io">Electron</a>. It was developed by Github for use with their <a href="http://atom.io">Atom</a> text editor, but has become a generalized tool for creating cross platform applications using web technologies like HTML, CSS, and Javascript.</p>
<p>Using Electron, Undercast creates a sandboxed instance of <a href="https://overcast.fm/">Overcast.fm</a>'s website. As far as the website is concerned, it's just been opened in a standard browser instance. Undercast then injects a small javascript API into the Overcast instance to enable media key controls. The menubar aspects of Electron are handled by <a href="https://github.com/maxogden">maxogden</a>'s <a href="https://github.com/maxogden/menubar">menubar</a> package.</p>
<h1 id="gettingundercast">Getting Undercast</h1>
<p>Precompiled builds of Undercast are located in the <code>/releases</code> folder. Simply unzip them and launch the .app or .exe file.</p>
<p><a href="https://github.com/ImStuBTW/undercast/raw/master/releases/Undercast.app.zip">Current Mac Release</a></p>
<p><a href="https://github.com/ImStuBTW/undercast/raw/master/releases/undercast-win32-x64.zip">Current Windows Release</a></p>
<h1 id="buildingityourself">Building It Yourself</h1>
<p>Undercast is a standard straightforward Electron application. Run <code>npm install</code> to download the necessary Node packages, then run <code>./node_modules/.bin/electron .</code> to launch the application. Compiling the application for production can be done with the <code>electron-packager</code> npm module.</p>
<h1 id="disclaimer">Disclaimer</h1>
<p>Undercast is not an official Overcast application. It has no relation to Marco Arment, and offers no guarantee that it will continue working in the future. Undercast is a free open source project available under the MIT license.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Chrono, the Cron-to-English Cron Job Builder]]></title><description><![CDATA[Chrono is a small Cron-To-English web app to help you set reoccurring system tasks on Linux machines.]]></description><link>https://stuart-jones.com/blog/the-cron-to-english-cron-job-builder/</link><guid isPermaLink="false">5e913bf4017a114be0c9605b</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 16 Nov 2016 22:10:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.38.00-PM-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><iframe src="https://stuart-jones.com/chrono/" width="100%" height="500px" scrolling="yes" frameborder="0"></iframe>
<img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.38.00-PM-1.png" alt="Chrono, the Cron-to-English Cron Job Builder"><p>Cron is a popular reoccurring task scheduler for Unix-based computer systems. The concept is relatively simple: Create a .crontab file with your scheduled events, and an omnipresent system daemon executes the command at the correct time. Cron has been around since the 1970s, so the crontab's file format has seen plenty of use outside of Cron itself. Being such an old format, Cron can take a while to completely wrap your head around. Set your brackets and braces aside, Cron works completely off of whitespace and newline characters.</p>
<p>Here's an example of your typical .crontab file:</p>
<pre><code class="language-nohighlight">30 08 10 06 * /home/john/full-backup
00 11,16 * * * /home/john/bin/incremental-backup
00 09-18 * * * /home/jane/check-db-status
</code></pre>
<p>The first five entries designate the times the job should run, and the last entry is the command that should be run. Cron's manfile gives a brief rundown of what which number signifies:</p>
<pre><code class="language-nohighlight"># ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │                                       7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * *  command to execute
</code></pre>
<p>In addition to numbers, Cron also accepts * wildcards, and will also allow ranges of values (1-5) and comma separated lists of values (1,3,6,8).</p>
<p>Some cron jobs are easier to build than others. It's fairly easy to determine that <code>0 0 * * *</code> means midnight every night, but it's less evident that <code>0 0 * 1 0</code> means midnight on every Monday in January. To double-check their work, programmers often turn to Cron-to-English tools to ensure their task will run at the correct interval.</p>
<p>I've been learning <a href="https://facebook.github.io/react/">React</a> recently, and realized that a such a converter would be a nice way to test the React skills I had learned without needing to code an extensive backend. The end result was <a href="https://stuart-jones.com/chrono">Chrono</a>, a simple Cron-to-English Cron Job Builder.</p>
<p>Chrono takes a standard 5 field Cron job, validates the entries, and then converts it to English. It also provides a field-by-field explanation of valid values. It can be used in the embed above, or viewed directly using the link.</p>
<p>Building Chrono was relatively straightforward. It uses the latest version of <a href="https://facebook.github.io/react/">React</a> (15.3), and the ES2015 and JSX javascript standards. I used the <a href="http://gulpjs.com">Gulp task runner</a> to convert the ES2015 scripts to standard ES5 javascript using the <a href="http://babeljs.io">Babel polyfill</a>. For styling the page, I used the <a href="http://getbootstrap.com">Bootstrap</a> framework and the <a href="http://sass-lang.com">Sass</a> CSS Preprocessor. For converting the cron jobs to plain English, I used azza-bazoo's <a href="https://www.npmjs.com/package/prettycron">PrettyCron</a> NPM package. This package was converted for use with React by <a href="http://browserify.org">Browserify</a>. Finally, the validation of the various cron fields was done by this <a href="http://stackoverflow.com/a/21558119">lengthy RegEx</a>. The code for Chrono can be found on my <a href="https://github.com/ImStuBTW/chrono">Github repository</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato]]></title><description><![CDATA[While it's still hard to get into the mood for a hearty stew when it's 90 degrees outside, it would be a shame to let some of these seasonal fall ingredients go to waste.]]></description><link>https://stuart-jones.com/blog/fall-breakfast-tacos-with-chorizo-pumpkin-seed-and-sweet-potato/</link><guid isPermaLink="false">5e913bf4017a114be0c9605a</guid><category><![CDATA[Food]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Mon, 17 Oct 2016 03:01:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/IMG_5653-copy-2-1.jpg" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/IMG_5653-copy-2.jpg" class="kg-image" alt="Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato"></figure><img src="https://stuart-jones.com/content/images/2020/04/IMG_5653-copy-2-1.jpg" alt="Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato"><p>The weather has finally fallen under 90° here in Austin, which means less that Fall is here, and more that Fall ingredients have finally started showing up in grocery stores. While it's still hard to get into the mood for a hearty stew this time of year, it would be a shame to let some of these seasonal ingredients go to waste.</p><p>To take advantage of these Fall ingredients, I've decided to turn to the humble potato hash. More often than not it simply functions as refrigerator velcro to suck up leftover ingredients at the end of the week, but it can easily be repurposed as an upscale showcase for seasonal harvests.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/batches.png" class="kg-image" alt="Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato"></figure><p>To start with, my boyfriend and I had plenty over pumpkin seeds leftover from his <a href="http://www.mageuzi.com/blog/2016/10/18/pumpkin-coconut-curry-with-crispy-salmon/">pumpkin coconut curry with crispy salmon</a>. With that as the starting point, everything else quickly fell into place. Sweet potato fit the time of year, and works well with the seeds to bring a nice sweetness to the dish. Spanish chorizo was an obvious smokey counterbalance. Cured meats work well in potato hashes, this isn't the standard crumbly Mexican chorizo you're likely to see around town. Some sort of onion always helps bring a hash together, so I decided to stick with my typical yellow onion for this recipe. Finally, some sliced radish on top adds a nice light crunch to the finished product. Finally, all good hashes need an egg.</p><p>Because this is Austin, I served the hash as a taco. Call me a stereotype if you want, but breakfast tacos are amazing.</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/baked.png" class="kg-image" alt="Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato"></figure><p>Hashes are pretty forgiving to experimentation and tweaking. If you want to make this recipe vegetarian, switch out the chorizo for a diced poblano. If you want to make the hash vegan, do the same and omit the eggs. Just make sure your tortillas weren't made with lard.</p><p>The secret to this recipe is <a href="http://www.seriouseats.com/2014/04/the-food-lab-how-to-make-the-best-potato-hash.html">J. Kenji López-Alt's parboiling technique</a>. Boiling the potatoes a bit before adding them to the frying pan helps ensure that they're cooked all the way through, but it also tends to make the potatoes fall apart in the process. By adding a tablespoon of vinegar per quart of water, you slow the breakdown of the potatoes' pectin, and ensure that they hold together longer in the pan. Finally, by working in batches you can make sure that you don't overcrowd the pan, and that everything cooks through thoroughly.</p><h1 id="fall-breakfast-tacos-with-chorizo-pumpkin-seed-and-sweet-potato">Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato</h1><p>Makes: 6-8 Tacos</p><p>Prep Time: 10 Minutes</p><p>Total Time: 45 Minutes</p><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/IMG_5616-copy.jpg" class="kg-image" alt="Fall Breakfast Tacos with Chorizo, Pumpkin Seed, and Sweet Potato"></figure><h2 id="ingredients">Ingredients</h2><ul><li>1 to 1 1/2 lb Sweet Potato, Diced into 1/4 inch cubes</li><li>8 oz Spanish Chorizo, Diced into 1/4 inch cubes</li><li>6 oz Pumpkin Seeds</li><li>2-3 Radishes, Thinly Sliced</li><li>1 Small Yellow Onion, Diced</li><li>4 Eggs</li><li>1 tbps Cilantro</li><li>1-2 tbps White Vinegar</li><li>2 tsp Cumin</li><li>2 tsp Chili Powder (Spice Blend)</li><li>2 tsp Chile Powder (Ground Chile Pepper)</li><li>4-8 of Your Preferred Tortillas. Flour, Corn, Wheat, Whatever. Anything works.</li></ul><h2 id="steps">Steps</h2><ol><li>Preheat the oven to 375°F. Peel and dice the sweet potato into 1/4 inch cubes. Dice the chorizo into 1/4 inch chunks. Medium dice the onion. Chop the tops and bottoms off the radishes and thinly dice. Remove 1 tbps of cilantro leaves from the stems and roughly chop.</li><li>Bring a pot of water to a boil and add 1 tbps of white vinegar. Add the sweet potatoes, wait for the water to come back to a boil, and cook for five minutes. Drain the potatoes with a strainer and rinse with cold water to stop the cooking.</li><li>While the sweet potatoes boil, heat a large non-stick pan on medium heat. Toss the pumpkin seeds with olive oil, salt, pepper, 1tbs cumin, and 1tbs of the chili powder of your choice. Cook for 2-4 minutes, until the pumpkin seeds are brown and fragrant. Transfer to a large bowl.</li><li>Add the chorizo chunks to the medium heat skillet. Stir occasionally for 6-8 minutes, until the chorizo is crispy on all sides and the fat has rendered out. Using a slotted spoon, remove the chorizo and set aside with the pumpkin seeds, leaving the fat in the skillet.</li><li>Once the sweet potato has drained, add it to the skillet with a few ounces of olive oil if necessary. Stir occasionally for about 20 minutes, until the potatoes are crispy and brown on all sides. Season with salt, pepper, 1tbs cumin, and 1tbs chile powder. Stir for 30 seconds. Transfer the potatoes to the bowl with the pumpkin seeds and chorizo.</li><li>Add additional olive oil and the diced onion to the skillet, stirring occasionally for 2-4 minutes until brown and soft. Transfer onions to the bowl with the rest of the ingredients.</li><li>Add all but a pinch of the cilantro to the bowl of ingredients. Toss the contents of the bowl thoroughly and return to the skillet.</li><li>Make four wells in the potato hash. Carefully crack the four eggs into each of the four wells, being careful not to lose any shell in the hash. Salt and pepper the eggs, drizzling with a dash of olive oil.</li><li>Transfer the skillet to the oven. After 10 minutes at 375°F, the whites of the egg should be set while the yokes are still runny.</li><li>While the hash bakes, heat the tortillas on a spare skillet, grill plate, or in the microwave. If using the stove top, heat each side of the tortilla for approximately a minute on each side. If using the microwave, wrap with paper towels and microwave for 30 seconds.</li><li>Once the hash is done baking, assemble the tacos. Garnish with diced radish and chopped cilantro. Serve immediately.</li></ol><p>This recipe can also be found on <a href="https://projectnom.com/recipe/436">Project Nom</a>.</p>]]></content:encoded></item><item><title><![CDATA[Steam Storefront Slackbot: Gabe]]></title><description><![CDATA[Gabe is a simple Slack bot for searching the Steam storefront. When a user inputs the '/steam' command along with a game title, Gabe will reply with a brief summary of the game, along with a link to the game's Steam page.]]></description><link>https://stuart-jones.com/blog/steam-storefront-slackbot-gabe/</link><guid isPermaLink="false">5e913bf4017a114be0c96059</guid><category><![CDATA[Projects]]></category><category><![CDATA[Video-Games]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Tue, 10 May 2016 23:56:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Gabe-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Gabe.png" class="kg-image" alt="Steam Storefront Slackbot: Gabe"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Gabe-1.png" alt="Steam Storefront Slackbot: Gabe"><p><a href="https://github.com/ImStuBTW/gabe">Gabe</a> is a simple Slack bot for searching the Steam storefront. When a user inputs the '/steam' command along with a game title, Gabe will reply with a brief summary of the game, along with a link to the game's Steam page.</p>
<h2 id="requirements">Requirements</h2>
<p>This Slack bot is a NodeJS app. It's got a few dependencies, which it fetches through npm. Gabe uses Express to route Slack commands. The Steam store data is fetched through NAME's excellent <a href="https://www.npmjs.com/package/steamstore">steam-store</a> utility. Steam technically doesn't have an API for the online store, steam-store is using an undocumented API Steam uses for the Big Picture mode. This API doesn't have a search functionality, so Gabe has to download the entire app list when it first runs. This list is stored in a sqlite3 database, and is updated using the <a href="https://www.npmjs.com/package/cron">cron</a> package at midnight and noon.</p>
<p>I've also included a Procfile in this repository. You can run Gabe anywhere, but Slack requires apps using /slash commands to be hosted on a server with SSL. Heroku makes an ideal place since they provide SSL out of the box.</p>
<h2 id="slackconfiguration">Slack Configuration</h2>
<p>You'll need to set up two Custom Integrations in Slack. First create a Slash Command with <code>/steam</code> for the command, <code>https://YourServer.com/steam</code> for the URL, POST for the Method, and whatever Name and Icon you'd like. Next, create a an Incoming Webhook. Set the Channel to the channel you'd like the command to work in, and then conigure the name and icon with the same name and icon as you used for the Slash Command.</p>
<p>Steambot.js needs to use the Incoming Webhook URL in the send() function. You'll either need to insert everything after <code>https://hooks.slack.com/services</code> as a variable into the source code (including the <code>/</code> character), or you'll need to set your system's <code>process.env.INCOMING_WEBHOOK_PATH</code> to that same variable.</p>
<h2 id="buildandrun">Build and Run</h2>
<p>Gabe's build process is fairly standard. <code>npm install</code>, then <code>node app</code>. If you're going to run Gabe on Heroku, commit the code to a local git repository, then run <code>heroku create</code> and <code>git push heroku master</code>. You'll also have to set Heroku's config vars to have <code>INCOMING_WEBHOOK_PATH</code> set to your Slack's incoming webhook path variable. This can be done through Heroku's web console, or through the command <code>heroku config:set INCOMING_WEBHOOK_PATH=[Everything after &quot;https://hooks.slack.com/services&quot; in Slack's Incoming Webhook URL]</code>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Messaging UI Concept: Bruno]]></title><description><![CDATA[Bruno is a conversational UI concept that lives completely in your web browser. It loosely mimics the iOS Messages interface. It supports image attachments, gifs, emoji, and links. Rather than allowing full text entry, Bruno allows the user to choose from a fixed set of replies. ]]></description><link>https://stuart-jones.com/blog/messaging-ui-concept-bruno/</link><guid isPermaLink="false">5e913bf4017a114be0c96058</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 04 May 2016 22:15:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-21-at-9.11.01-AM.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><iframe src="https://stuart-jones.com/bruno" width="100%" height="800px" scrolling="yes" frameborder="0"></iframe>
<img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-21-at-9.11.01-AM.png" alt="Messaging UI Concept: Bruno"><p>Using computers to message each other isn't exactly a new concept. From the dawn of networked machines, we've been using a variety of tools to communicate with each other. Be it email, IRC, IM, SMS, or one of the relative newcomers like social networks, group messaging apps, and persistent team chats like Slack, we've gotten pretty good at letting each other know what we're up to.</p>
<p>One newer wrinkle in this paradigm is human-to-machine messaging. Rather than using a messaging app to talk to another human being, your messages are being sent to a fully or partially automated computer script. These services can vary in complexity, ranging from something as primitive as a phone tree, or scaling all the way up to natural language processing with something like <a href="http://www.ibm.com/smarterplanet/us/en/ibmwatson/">IBM's Watson</a>.</p>
<p>These new services have started to be colloquially referred to as &quot;Bots&quot;. Services like Siri and Slack have gotten people used to talking to machines, and so designers have begun to focus more attention on these new &quot;Conversational UIs&quot;. There have already been a few interesting projects to come out of this new focus, such as the <a href="https://itunes.apple.com/us/app/lifeline.../id982354972">Lifeline</a> mobile game, or <a href="http://qz.com/613700/its-here-quartzs-first-news-app-for-iphone/">Quart's new iOS news app</a>.</p>
<p>That's where Bruno comes in.</p>
<h2 id="bruno">Bruno</h2>
<p>Bruno is a conversational UI concept that lives completely in your web browser. It loosely mimics the iOS Messages interface. It supports image attachments, gifs, emoji, and links. Rather than allowing full text entry, Bruno allows the user to choose from a fixed set of replies.</p>
<p>This UI mockup was a chance for me to learn a variety of newer web technologies, it probably won't work if your browser doesn't suppose <a href="https://babeljs.io/docs/learn-es2015/">ES2015</a>. Bruno uses <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations">CSS3 Animations</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> for flow control, <a href="http://emojione.com">Emoji One</a> for cross platform emoji support, and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes">Flexboxes</a> to keep everything nice and centered. The messages are currently a fixed script of <a href="https://jquery.com">JQuery</a>-parsed .json files, but he was designed modularly so that a more complicated message source could be implemented later.</p>
<p>I just referred to my program as a 'he'. The future is weird.</p>
<p>If you'd like to try out Bruno, feel free to use the embed below, or view it full screen <a href="https://stuart-jones.com/bruno">here</a>. His- err, the page's source is also up on <a href="http://github.com/ImStuBTW/bruno">Github</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Some Old Projects]]></title><description><![CDATA[I've already highlighted a few of my favorite projects on this site, but I wanted to take a minute to post about some of my older college works. Enjoy.]]></description><link>https://stuart-jones.com/blog/some-old-projects/</link><guid isPermaLink="false">5e913bf4017a114be0c96057</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 04 May 2016 20:24:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/IMG_0129-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/IMG_0129-2.jpg" alt="Some Old Projects"><p>I've already highlighted a few of my favorite projects on this site, but I wanted to take a minute to post about some of my older college works. Enjoy.</p>
<h2 id="piot">PiOT</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://stuart-jones.com/content/images/2020/04/PiOT-1.jpg" width="3264" height="2448" alt="Some Old Projects"></div><div class="kg-gallery-image"><img src="https://stuart-jones.com/content/images/2020/04/Pebble.jpg" width="2448" height="3264" alt="Some Old Projects"></div></div></div></figure><!--kg-card-begin: markdown--><p>It's an <em><strong>I</strong></em>nternet <em><strong>O</strong></em>f <em><strong>T</strong></em>hings sensor based off of a Raspberry <em>Pi</em>. Get it?</p>
<p>The PiOT is a data capture device for a do-it-yourself IoT setup. It's equipped with a pair of DS18B20 temperature sensors, a DHT22 combination  temperature/humity sensor, and an optional Raspberry Pi camera.</p>
<p>The sensors are read and logged on a five minute interval. This data is uploaded to Sparkfun's <a href="https://stuart-jones.com/blog/some-old-projects/data.sparkfun.com">data.sparkfun.com</a> data logging service, or a personal hosted instance of the underlying <a href="http://phant.io/">Phant</a> service. From there, data.sparkfun.com and Phant can serve up .json files to a variety of endpoints, including a web dashboard, mobile app, or even a smartwatch.</p>
<p>It was a fun project combining several of my favorite technologies. I got to solder a sensor together, write some Python scripts for the sensors, design a Chart.js and Bootstrap web dashboard, and even learn how to write <a href="http://pebble.com">Pebble</a> smartwatch faces.</p>
<h1 id="movieknight">Movie Knight</h1>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Movie-Knight.png" class="kg-image" alt="Some Old Projects"></figure><!--kg-card-begin: markdown--><p>Movie Knight was my senior project. Working with a group of classmates, we followed standard Agile and SCRUM methodologies to create an 2.3 Android-based movie recommendation app.</p>
<p>The app was a least-grief recommendation service for groups. You and your friends would enter your movie collections into the app, giving each film a 5-star (with half increment) rating. Then, you would specify which users were attempting to watch a movie together, and the app would recommend movies that best fit the entire group's tastes.</p>
<p>I handled the majority of the work writing the Android app. Another classmate handled the database logic, and finally our final group member wrote the recommendation engine. It was a fun project, and gave a good amount of insight into mobile app development.</p>
<h1 id="diceroller">Dice Roller</h1>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Dice-Roller.png" class="kg-image" alt="Some Old Projects"></figure><!--kg-card-begin: markdown--><p>This one might take a bit of explaining. My school had a Computer Human Interaction course. It was a fascinating class that taught Computer Scientists to use Humanities-style skills in order to study users more efficiently. As part of the class, we had to study a group of people in a culture we didn't belong to. These groups ranged from student athletes to bingo hall regulars. I had never played a tabletop RPG before, so I decided to study my friends during their weekly Dungeons and Dragons game.</p>
<p>I got to hang out with friends for homework, how cool is that?</p>
<p>The final project of the class was open ended. All we had to do was to create something that would benefit the group we had studied. I had recently been teaching myself the Arduino platform, so I decided to put those skills to good use.</p>
<p>I ended up creating an electronic dice roller. Tabletop games like Dungeons and Dragons often require users to roll all sorts of non-standard dice. The device I created could simulate any number of dice rolls, and automatically integrate a player's skill modifiers into the final result. It even had a motion sensor in it so that you could mimic the sensation of rolling dice.</p>
<p>My favorite part of the project was the enclosure. I used <a href="http://ponoko.com">Ponoko.com</a> to laser-cut a custom enclosure I had created in the 3D modeling software <a href="http://www.sketchup.com">Sketchup</a>. It definitely helped turn heads during the final presentation.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The When and Whys of the Bootstrap Grid]]></title><description><![CDATA[Bootstrap 3's documentation is good at showing you how to use its grid system, but it's not that great at telling you when or why you might want to use some of the grid system's various features. This abridged guide is going to try and help fill in some of those gaps.]]></description><link>https://stuart-jones.com/blog/the-when-and-whys-of-the-bootstrap-grid/</link><guid isPermaLink="false">5e913bf4017a114be0c96056</guid><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 16 Apr 2016 17:25:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.31.59-PM-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Screen-Shot-2020-04-10-at-11.31.59-PM-1.png" alt="The When and Whys of the Bootstrap Grid"><p>Bootstrap 3's documentation is good at showing you <em>how</em> to use its grid system, but it's not that great at telling you when or why you might want to use some of the grid system's various features. This abridged guide is going to try and help fill in some of those gaps, and point out some beginner pitfalls of the Bootstrap grid.</p>
<h2 id="wrappers">Wrappers</h2>
<p>Before we can get started discussing the grid itself, we first need to briefly go over some of Bootstrap's wrapper elements. Bootstrap offers two primary page wrappers <code>.container</code> and <code>.container-fluid</code>. <code>.container</code> has a fixed width, while <code>.container-fluid</code> always take up the full width of the page. Bootstrap expects one of these two divs to be wrapped around your page's primary content. The grid system won't work without it.</p>
<p>One of the common pitfalls with Bootstrap's <code>.container</code> is not knowing when to break them up into separate elements. It's rare that you'll just have one container covering the entire page.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		// This rarely happens.
	&lt;/div&gt;
&lt;/body&gt;
&lt;html&gt;
</code></pre>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;nav class=&quot;navbar&quot;&gt;
		&lt;div class=&quot;container&quot;&gt;
		&lt;/div&gt;
	&lt;/nav&gt;
	&lt;div id=&quot;main&quot;&gt;
		&lt;div class=&quot;container&quot;&gt;
			// Main content goes here.
			// The typical Bootstrap layout has a few containers.
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;div id=&quot;footer&quot;&gt;
		&lt;div class=&quot;container&quot;&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>The second wrapper is a much shorter discussion. When working with the grid system, you need to place your grid within a <code>.row</code> div. The Bootstrap documentation dedicates all of 6 words to this vital point. <em>Place grid columns in any <code>.row</code>.</em></p>
<h2 id="thegrid">The Grid</h2>
<p>Alright, with wrappers out of the way, we can move onto the grid itself. The Bootstrap grid system has 12 column slots that are typically displayed side-by-side. It uses CSS media queries to stack these columns on top of each other when your browser window gets small enough to pass a certain breakpoint.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		&lt;div class=&quot;row&quot;&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-1&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-44-26-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-45-00-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>You can set which breakpoint you'd like the columns to start stacking at by changing the variable in the middle of the column. <code>.col-lg-1</code> is shown side by side with the other columns when the browser is 1170px or larger, <code>.col-md-1</code> shows side by side with other columns when the page is 970px or larger, <code>.col-sm-1</code> shows side by side with other columns when the page is 750px or larger, and <code>.col-xs-1</code> is <em>always</em> shown as a side-by-side column. Columns using <code>.col-xs-1</code> will never be stacked, no matter how small the browser window gets.</p>
<p>Of course, page layouts are almost never twelve individual columns of content. Most page layouts take advantage of about two or three columns. To create a layout with only a few columns, we'll need to adjust the size variable at the end of our column class. Instead of using 12 <code>.col-md-1</code> columns, we'll use just two columns: <code>.col-md-8</code> and <code>.col-md-4</code>. The number at the end represents how many columns wide a div is. For most layouts, you'll want all of your column sizes to add up to 12.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		&lt;div class=&quot;row&quot;&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-8&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-4&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-32-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-45-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Columns can have different sizes at different breakpoints. Take for instance the <code>.col-md-8</code>, <code>.col-md-4</code> layout shown above. If you wanted the <code>.col-md-4</code> sidebar to render narrower on a smaller screen, you should just add the sm class after the md class. <code>.col-md-8 .col-sm-9</code> and <code>.col-md-4 .col-sm-3</code> would feature a thick sidebar at lg and md breakpoints, a narrower sidebar at the sm breakpoint, and would stack at the xs level. Just remember that you still would generally want your columns to add up to 12 at whatever size they're at.</p>
<h2 id="columnwrapping">Column Wrapping</h2>
<p>There is one exception to the add-up-to-12 rule, which the &quot;Column Wrapping&quot; section of the Bootstrap docs tries to explain. If you've got a <code>.row</code> that is more than 12 columns wide, the overflow columns will be wrapped around to the next line. A trio of <code>.col-md-6</code> columns would render as two boxes side by side, with a third box underneath the first. This technique is useful for when you're rendering <code>n</code> items from a database, and don't know how many items are going to be retrieved. You'll frequently see it used in galleries and similar layouts. The overflowed columns will still stack on on small screens as before.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
	&lt;div class=&quot;container&quot;&gt;
		&lt;div class=&quot;row&quot;&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-6&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-6&lt;/div&gt;
			&lt;div class=&quot;col-md-1&quot;&gt;.col-md-6&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-10-56-17-AM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-10-56-32-AM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<h2 id="offsetcolumns">Offset Columns</h2>
<p>Offset columns are fairly straightforward. They create a gap in your column layout. <code>.col-md-4</code> and <code>.col-md-4</code> would usually create two blocks that are four columns wide, with a four column wide space after the two blocks. If you instead have a <code>.col-md-4</code> div and a <code>.col-md-4 .col-md-offset-4</code> div, the second block will be pushed over 4 spaces. The page will render with a block 4 columns wide, a space 4 columns wide, and then a block 4 columns wide. The xs/sm/md/lg sizing in the col-*-offset-4 div refers to what screen size the offset should happen on. The divs <code>.col-sm-6</code> <code>.col-md-4</code> and <code>.col-sm-6 .col-md-4 .col-offset-4</code> will be stacked on xs screens, side by side on sm screens, and separated by a 4 column gap on screens md and above. I don't really use this feature much, but it can be useful for certain layouts.</p>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-46-01-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-46-47-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Large Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-47-33-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Window:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-47-52-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<h2 id="nestingcolumns">Nesting Columns</h2>
<p>Nesting columns is one of the tricky mental leaps to make with grid systems. It's often not strictly necessary, but can help logically separate your page better. For instance, if your page layout has a main content area and a sidebar, you'd probably use a <code>.col-md-8</code> and <code>.col-md-4</code> div. If for whatever reason you needed to break your main content area up into another pair of equal divs, you generally <em>would not</em> want to change the page layout to be a <code>.col-md-4</code> <code>.col-md-4</code> <code>.col-md-4</code> grid. Instead, you would want to keep the <code>.col-md-8</code> <code>.col-md-4</code> layout, and nest a new grid inside of the main col-md-8 content area. The nested grid will need its own <code>.row</code> element, but it doesn't need another <code>.container</code> wrapper.</p>
<pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
&lt;div class=&quot;container&quot;&gt;
	&lt;div class=&quot;row&quot;&gt;
		&lt;div class=&quot;col-md-8&quot;&gt;
			&lt;p&gt;.col-md-8&lt;/p&gt;
			&lt;div class=&quot;row&quot;&gt;
				&lt;div class=&quot;col-md-6&quot;&gt;
					.col-md-6
				&lt;/div&gt;
				&lt;div class=&quot;col-md-6&quot;&gt;
					.col-md-6
				&lt;/div&gt;
			&lt;/div&gt;
		&lt;/div&gt;
		&lt;div class=&quot;col-md-4&quot;&gt;
			.col-md-4
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Large Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-50-31-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-50-57-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>The new nested divs still needs to add up to 12, not the width of the parent column. After all, you're creating a whole new set of columns. So inside of your <code>.col-md-8</code> column's <code>.row</code> wrapper, you would want to put a set of <code>.col-md-6</code> <code>.col-md-6</code> divs. If you wanted to make the main content area temporarily have three columns, you'd instead use <code>.col-md-4</code> <code>.col-md-4</code> <code>.col-md-4</code>, etc.</p>
<h2 id="columnordering">Column Ordering</h2>
<p>Finally there's column ordering. When using Bootstrap's grid system, your columns will be displayed in the order they're declared. When unstacked, they'll be shown in order from left to right, and then they're stacked they'll be shown top to bottom. The only way to change the stacking order is to change the order in which your columns are declared in your HTML code.</p>
<p>In the unstacked, side-by-side view however, it is possible to change the order of the columns. Using the <code>.col-md-8</code> <code>.col-md-4</code> example from before, let's say that you wanted the <code>col-md-8</code> column to stack on top of the <code>.col-md-4</code> column, but you wanted the <code>.col-md-4</code> column to render on the left of the <code>.col-md-8</code> column when on larger pages.</p>
<p>To do this, you would need to use the <code>.col-md-pull-* </code> and <code>.col-md-push-* </code> helper classes. By changing your columns to <code>.col-md-8 .col-md-push-4</code> and <code>.col-md-4 .col-md-pull-8</code>, the larger column would be pushed out an extra 4 column lengths, and the smaller column would be pulled in 8 column lengths. The end result would be that the columns would switch places when viewed at md breakpoints and above.</p>
<pre><code class="language-html">&lt;div class=&quot;row&quot;&gt;
	&lt;div class=&quot;col-md-8 col-md-push-4&quot;&gt;.col-md-8 .col-md-push-4&lt;/div&gt;
	&lt;div class=&quot;col-md-4 col-md-pull-8&quot;&gt;.col-md-4 .col-md-pull-8&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Large Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-32-PM-1.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-45-PM-1.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Large Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-53-02-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<p>Small Screen:<br>
<img src="https://stuart-jones.com/content/images/2016/04/Screen-Shot-2016-04-06-at-12-52-49-PM.png" alt="The When and Whys of the Bootstrap Grid"></p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope these explanations make sense. It can take a while to get used to combining all of these different techniques together. Make sure to check out the Bootstrap documentation to see some of the more advanced options I didn't have a chance to go over here.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Tumblr Theme: Wallace, A Bootstrap Theme]]></title><description><![CDATA[A Bootstrap-based Tumblr theme template.]]></description><link>https://stuart-jones.com/blog/tumblr-theme-wallace-a-bootstrap-theme/</link><guid isPermaLink="false">5e913bf4017a114be0c96054</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Wed, 16 Dec 2015 06:12:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/ScreenShot-3-1.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Wallace.png" class="kg-image" alt="Tumblr Theme: Wallace, A Bootstrap Theme"></figure><!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/ScreenShot-3-1.png" alt="Tumblr Theme: Wallace, A Bootstrap Theme"><p>I recently looked into what it takes to theme a Tumblr site. While there were a few existing Bootstrap-based Tumblr themes, none of them were that straightforward to use or customize. I decided to throw this theme together for anyone else who happens to be looking for a Bootstrap-based Tumblr theme.</p>
<h2 id="tldr">tl;dr</h2>
<p>Take the index.html file in the /dist folder and upload it to the &quot;Edit Theme&quot; section of Tumblr. That’s it. You’re done.</p>
<h2 id="thehardway">The Hard Way</h2>
<p>Wallace provides two methods for styling the theme. Option 1 is the simple method described above. You fetch the Bootstrap.min.css file from a CDN, and the theme’s header contains the few Tumblr specific stylings required to make the page render correctly.</p>
<p>Option 2 is a bit more tricky. Due to the popularity of Bootstrap, sites powered by the framework have all started to look the same. To combat this, Bootstrap offers several different methods of theming the framework. The most simplistic method uses <a href="http://getbootstrap.com/customize/">getbootstrap.com/customize/</a>, while the slightly more advanced methods rely on the LESS and Sass CSS preprocessors. CSS Preprocessors act as a superset of CSS, allowing you to take advantage of more advanced features such as variables, inheritance, and mixins.</p>
<p>Wallace is designed to take advantage of <a href="http://sass-lang.com">Sass</a>. It uses <a href="https://nodejs.org/">NodeJS’s</a> <a href="https://github.com/sass/libsass">LibSass</a> to compile Sass files, aided by the <a href="http://gruntjs.com">Grunt</a> task runner and the <a href="http://bower.io">Bower</a> package manager. If you’d like to theme Wallace, you’ll need a system with Node, Grunt, and Bower installed. Node’s <a href="https://nodejs.org">website</a> has everything you need to install the scripting language. Once that’s done, you’ll want to install Grunt and Bower globally on your development system.</p>
<pre><code>npm install grunt-cli -g
npm install bower -g
</code></pre>
<p>Now that you’v gotten that taken care of, you can let Wallace’s install script’s do it’s thing. Execute the NPM package.json file, then Bower’s bower.son file.</p>
<pre><code>npm install
bower install
</code></pre>
<p>Finally, all you need to do is run the Gruntfile.js. Grunt will take the ./sass/styles.scss file and compile it down into the ./css/styles.css file.</p>
<p><code>grunt</code></p>
<p>styles.<em>scss</em> already has a @import command for the _bootstrap.scss file that Bower fetched. styles.<em>css</em> is the only stylesheet your Tumblr theme will need. Go to the “Edit Appearance” section of Tumblr, then click “Edit Theme”. Click “Edit HTML”, then copy and paste Wallace’s Index.html file into the window. Click the gear in the top left corner and then click “Theme Assets”. Upload your compiled style.css file here. Find the section on line 164 that reads “[REPLACE WITH TUMBLR UPLOADED CSS FILE]”. Highlight this text, then slick the “Insert” link next to the styles.css file you uploaded. Click “Update Preview”, and then “Save” at the top of the coding pane. Wallace should now be successfully applied to your Tumblr blog.</p>
<h2 id="license">License</h2>
<p>Wallace is licensed under The MIT License (MIT). It is based in part on the excellent <a href="http://buildthemes.tumblr.com">Build Themes</a> ebook by Rohan Chandra.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Ghost Theme: Ninamori]]></title><description><![CDATA[A simple and clean theme for Ghost using Twitter Bootstrap 3, Font Awesome Icons, SASS, Bower, and Grunt.]]></description><link>https://stuart-jones.com/blog/ghost-theme-ninamori/</link><guid isPermaLink="false">5e913bf4017a114be0c96052</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 09 May 2015 22:44:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/Home-Page-2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/Home-Page-2.png" alt="Ghost Theme: Ninamori"><p>In my <a href="https://stuart-jones.com/howdy-world/">introductory blog</a> post, I explained the reasoning behind me jumping ship to the Ghost blogging platform. I always strive to give my sites the personal touch, so went and made my own Ghost theme to go along with my Stuart-Jones.com blog. If you're taking a peek under the hood, feel free to go look over at my <a href="https://github.com/imstuartjones/ninamori">Github entry</a> for:</p>
<h1 id="ninamori">Ninamori</h1>
<p>A simple and clean theme for Ghost using Twitter Bootstrap 3, Font Awesome Icons, SASS, Bower, and Grunt.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Home-Page-3.png" class="kg-image" alt="Ghost Theme: Ninamori"></figure><!--kg-card-begin: markdown--><h2 id="features">Features</h2>
<p>Ninamori was designed and tested for version 0.6 of the Ghost blogging platform. It supports the standard theme features of Ghost up until that point, including:</p>
<ul>
<li>Blog Image</li>
<li>Blog Cover</li>
<li>Customizable Navbar</li>
<li>Tags</li>
</ul>
<p>Ninamori was designed as a single user portfolio site theme, so it does not show post authors.</p>
<p>In addition to these standard features, Ninamori also includes:</p>
<ul>
<li>Auto-hiding navbar with scroll-up reveal</li>
<li>Tag specific cover images</li>
<li>Link-Blog Support</li>
</ul>
<h2 id="demo">Demo</h2>
<p>Ninamori powers my site! Check it out at /</p>
<h2 id="gettingstarted">Getting Started</h2>
<p>Ninamori uses <a href="http://bower.io/">Bower</a> for dependency management, and <a href="http://gruntjs.com/">Grunt</a> as the task runner. Both of these tools require Node and npm, which should already be installed on a system running Ghost. Instructions on how to use Bower and Grunt can be found on their respective websites. To get started using the theme, clone it to your Ghost's content/themes/ folder.</p>
<pre><code>git clone https://github.com/ImStuartJones/ninamori.git
</code></pre>
<p>Use NPM to install the required development packages.</p>
<pre><code>npm install
</code></pre>
<p>Next, run Bower to download the necessary web frameworks. (Bootstrap, Jquery, etc.)</p>
<pre><code>bower install
</code></pre>
<p>Bower should run and install the necessary packages into assets/bower_components/. Next we need to run Grunt to compile Ninamori's .sass and .js files.</p>
<pre><code>grunt
</code></pre>
<p>The gruntfile is also configured to be run in watch mode with the following task.</p>
<pre><code>grunt watch
</code></pre>
<p>Finally, Ninamori's gruntfile also supports BrowserSync. BrowserSync is a live browser reload package which streamlines web development and device testing. More information can be found at the <a href="https://stuart-jones.com/blog/ghost-theme-ninamori/www.browsersync.io">BrowserSync</a> website.</p>
<pre><code>grunt browsersync
</code></pre>
<p>Your Ninamori instance should be ready for Ghost to use at this point. Login to your Ghost instance and change the theme on the main page of the Settings screen.</p>
<h2 id="changingthetagcoverimages">Changing the Tag Cover Images</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://stuart-jones.com/content/images/2020/04/Post-View.png" class="kg-image" alt="Ghost Theme: Ninamori"></figure><!--kg-card-begin: markdown--><p>By default, Ninamori comes with a variety of cover images for posts based on their tag. The image files can be found in assets/images/. New tags can be defined in the partials/post_header.hbs file. It uses a simple Handlebar has/else waterfall, so a post with multile tags will use the first one referenced.</p>
<pre><code class="language-html">{{! Post Header has custom images based off of tags.}}
{{#post}}
{{#has tag=&quot;tech&quot;}}
&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-tech.jpg'}}');&quot;&gt;
{{else}}
	{{#has tag=&quot;board-games&quot;}}
		&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-board.jpg'}}');&quot;&gt;
	{{else}}
		{{#has tag=&quot;video-games&quot;}}
			&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-video.jpg'}}');&quot;&gt;
		{{else}}
			{{#has tag=&quot;lgbt&quot;}}
				&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-lgbt.jpg'}}');&quot;&gt;
			{{else}}
				{{#has tag=&quot;news&quot;}}
					&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg-news.jpg'}}');&quot;&gt;
				{{else}}
					&lt;div class=&quot;image-header-post&quot; style=&quot;background-image: url('{{asset 'images/post-bg.jpg'}}');&quot;&gt;
				{{/has}}
			{{/has}}
		{{/has}}
	{{/has}}
{{/has}}
{{/post}}
</code></pre>
<h2 id="licensing">Licensing</h2>
<p>Ninamori and its included images are covered under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Howdy World!]]></title><description><![CDATA[Welcome to the latest incarnation to my personal blog. As you may have guessed, I'm Stu. This is at least the third time I've reintroduced myself online. It seems customary at this point for developers to discuss the technology powering their site when they preform a relaunch, so here goes nothing.]]></description><link>https://stuart-jones.com/blog/howdy-world/</link><guid isPermaLink="false">5e913bf4017a114be0c96051</guid><dc:creator><![CDATA[Stuart Jones]]></dc:creator><pubDate>Sat, 09 May 2015 22:35:00 GMT</pubDate><media:content url="https://stuart-jones.com/content/images/2020/04/HelloWorld.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://stuart-jones.com/content/images/2020/04/HelloWorld.png" alt="Howdy World!"><p>Welcome to the latest incarnation to my personal blog. As you may have guessed, I'm Stu. This is at least the third time I've reintroduced myself online. It seems customary at this point for developers to discuss the technology powering their site when they preform a relaunch, so here goes nothing.</p>
<p>The current incarnation of Stuart-Jones.com is powered by the <a href="https://ghost.org/">Ghost</a> blogging engine. Ghost is a relatively new player in the blogging scene, but it's got a few things going for it. First and foremost, Ghost is <em>just</em> a blogging platform. They've very purposefully tried to avoid the CMS bloat that blog engines tend to experience over the years. Continuing the theme of keeping things simple, Ghost entries are based on the Markdown syntax. Markdown is minimalist syntax for formatting documents in a way that's easily read by humans, yet parseable into valid and simple HTML.</p>
<p>In a way, Ghost is a combination of my previous two attempts at a personal blog. My previous site was powered by <a href="http://jekyllrb.com">Jekyll</a>, a Ruby based static-site generator. The site before that was, as I imagine it was for most developers, a self hosted <a href="https://wordpress.org">Wordpress</a> page.</p>
<p>Wordpress is a fine and capable content management system that can handle just about anything you throw at it. This ironically can be an issue when all you want to <a href="https://ghost.org/about/">just blog</a>. Wordpress can host everything from a forum to a e-commerce shopping cart. While web hosts have <a href="https://kb.asmallorange.com/customer/portal/articles/1616356-install-wordpress-with-softaculous">bent over backwards</a> to make Wordpress simple, there can be a surprising number of hoops to jump through in order to get your post online. Pair that with a theming engine that requires you to not only know PHP, but know <a href="https://make.wordpress.org/core/handbook/coding-standards/php/">PHP the Wordpress way</a>, and you've got an environment ripe for competition.</p>
<p>This is where <a href="http://jekyllrb.com">Jekyll</a> comes in. Jekyll is a Ruby script which grew out of Github's README.md pages. Instead of its original purpose of converting a single Markdown file into a single HTML page, Jekyll converts entire folders of Markdown files into a site's worth of HTML files. It's about as drastic a shift away from Wordpress one can make. There's no longer a CMS to deal with. There's just a terminal command, some plugins, and the resulting <code>/_site</code> folder to upload. For a pure HMTL junkie, Jekyll is great. The site's template use the easy to learn <a href="http://handlebarsjs.com">Handlebars</a> system, and the resulting pages can be served up by anything from Apache and nginx to NodeJS and <a href="https://pages.github.com">Github Pages</a>.</p>
<p>If Jekyll is an over correction, Ghost is an attempt to right the ship. Jekyll's largest issue is that it requires you to install it locally on every system you want to publish on. This is fine is you work solely on *nix systems, but I spend a lot of time traveling solely with my Surface Pro 3. Once you've gotten Ghost set up on your web server, it's no-fuss console lets you write and publish blog posts from just about anywhere. Ghost's template engine uses the same straightforward Handlebars system as Jekyll, so for me there was very little downside in making the switch. Don't take my word for it though, here's what the <a href="http://themes.ghost.org">Ghost docs</a> have to say:</p>
<blockquote>
<p>Ghost is intended to be a happy medium between a completely dynamic CMS-style application, and a static file generator. The Ghost admin is a dynamic client side app, but the blog pages are generated server side and sent to the browser as static HTML. This makes Ghost themes super fast, and also allows for the blog pages to be heavily cached.</p>
</blockquote>
<p>If you're thinking of jumping ship from Wordpress to Jekyll, or Jekyll to Ghost, I highly suggest you give Ghost a chance. The underlying engine of Ghost is based off of <a href="https://nodejs.org">NodeJS</a>, which means you can test it out on the localhost of any modern OS, including Windows.</p>
<p>If you'd like to learn more about the Ghost theme powering this site, check out my writeup <a href="https://stuart-jones.com/ghost-theme-ninamori/">here</a>. Thanks for stopping by my site. Enjoy the ride.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>