<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Joshua Gilless</title>
    <description>The latest articles on Forem by Joshua Gilless (@joshuagilless).</description>
    <link>https://forem.com/joshuagilless</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F63942%2F4f16b707-cd75-45de-9034-1d320e5658ab.jpg</url>
      <title>Forem: Joshua Gilless</title>
      <link>https://forem.com/joshuagilless</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joshuagilless"/>
    <language>en</language>
    <item>
      <title>Generating Responsive Images for Your Small Website</title>
      <dc:creator>Joshua Gilless</dc:creator>
      <pubDate>Mon, 16 Mar 2020 20:18:11 +0000</pubDate>
      <link>https://forem.com/joshuagilless/generating-responsive-images-for-your-small-website-56ck</link>
      <guid>https://forem.com/joshuagilless/generating-responsive-images-for-your-small-website-56ck</guid>
      <description>&lt;p&gt;I love when things are easy in web development. For example, a responsive image is simple to create. You write your &lt;code&gt;&amp;lt;img class="img-responsive" src="..." alt="..." /&amp;gt;&lt;/code&gt; tag, throw it in your html and then write 4 lines of CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.img-responsive&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great! And you've even made it accessible because you wrote your alt text that described the image in a way that screen readers and people with slow connections will appreciate... Right?&lt;/p&gt;

&lt;h3&gt;
  
  
  Motivation
&lt;/h3&gt;

&lt;p&gt;My wife, Emma, is an animator and character designer, and I sometimes help her with her website. A big part of her website is her portfolio, which consists of images. Those images need to load for people who want to view them. I also care about hosting costs and would like to keep my bandwidth down. So when she sends me a 1.3MB, 5100x3300 pixel image, I need to make sure that I'm not sending that to a phone that's 414 pixels wide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Devices
&lt;/h3&gt;

&lt;p&gt;I have a Pixel 2, it's a great phone, I'd call it high end. I get that the Pixel 4 is out these days and there's several generations of Samsung galaxy out there that are the current top of the food chain in the Android market. As far as iPhones go, they're even better in terms of chip power. Looking at new phones right now (black friday sales), the lowest I can find an new iPhone 8 for is ~$350, the lowest new Pixel 3a is $250. These aren't the newest models by any means (iPhone is on 11 I think), these are the low end of what we can call great devices.&lt;/p&gt;

&lt;p&gt;But these are the bottom of the high end. When you go a tier down, you get cheaper and less performant devices. When you go to the top of the high end tier, you're getting devices that blow out the several year old laptop I have in terms of processing power and RAM.&lt;/p&gt;

&lt;p&gt;You could send the high end devices the 1.3MB image, and they'd load it fine. It would even be responsive because of our fancy 4 lines of CSS. The device could display it and scroll and handle all of it and we wouldn't have to give it a second thought.&lt;/p&gt;

&lt;p&gt;My wife and I have a phone plan where we pay by the Gigabyte up to a certain amount and then after that it's free. We usually use around 2GB per month and it's affordable. We bought our phones outright and so it keeps our monthly bill very low compared to a lot of other phone bills I've heard of. But again, we're at the bottom of the high end phone market. I know people using phones that nobody makes commercials for, phones that wouldn't fit the definition of a status symbol.&lt;/p&gt;

&lt;p&gt;A 1.3MB image works great on an iPhone 8 or a Pixel 2, but what about a 4 year old Motorola? It might be a little slow. When you're hosting images, you're paying for storage and bandwidth, but the person viewing them is also paying for an internet connection. It's a component of accessibility to make sure that you're costing the people viewing your site as little as possible, while still giving them the best quality image.&lt;/p&gt;

&lt;p&gt;The people viewing Emma's web site are mostly in America, people looking at it on relatively high end devices, with good internet connections. But if you're getting any traffic from Asia or Africa or South America, you're getting traffic from people who are using $30-$40 phones. The performance of those devices is not close to a late model iPhone. You have to consider accessibility as more than "Does this work on a screen reader?"&lt;/p&gt;

&lt;h3&gt;
  
  
  The Web Is Accessible
&lt;/h3&gt;

&lt;p&gt;Fortunately, accessibility is built in to the web, we just love to break it by doing things in a way that is inconsiderate to the people viewing the website.&lt;/p&gt;

&lt;p&gt;There's a lot of nuance for deciding how to present your images. A web site with lots of user generated content may decide to apply more compression to their images than a website that is an artist's portfolio hoping to get hired for making high quality art.&lt;/p&gt;

&lt;p&gt;Image format is also important to consider, a PNG compresses differently than a JPEG for example. Aspect ratio is also important to consider, a phone held vertically can display a taller image and keep all of it in the viewport more natively than a very widescreen monitor that might cut off the top and the bottom.&lt;/p&gt;

&lt;p&gt;Fortunately, because the web starts out accessible, we can get all the way there with that original block of CSS, and just using either the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element, or with adding to our &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element. We don't need to write JS around it until we want to add lazy-loading (another post for another time).&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting Questions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Do your users generate the image, or is it static content delivered by an artist?&lt;/li&gt;
&lt;li&gt;Do you need a different image to show up for different sized screens (art direction)?&lt;/li&gt;
&lt;li&gt;Does the layout of your site change around the image as you change screen sizes?&lt;/li&gt;
&lt;li&gt;Do we need to use the same image at different aspect ratios in different places?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What you end up doing will depend on each of these questions, and the grey area grows larger depending on the cases as we go along, you'll also find more nuanced questions to ask as follow up to each of those questions.&lt;/p&gt;

&lt;p&gt;Intelligently cropping is a hard problem. For example, let's say you want to use an image that a user has uploaded as a profile picture and display the full thing in one page, but if you shrink to mobile it changes to a photo of just their face. You'd need some kind of facial recognition to crop properly to handle doing that at scale. You can get very close with an upper-crop rather than a center-crop if it involves people, but you're still shooting fairly blind.&lt;/p&gt;

&lt;p&gt;For the rest of this post, let's go through something I did for a very specific case of a portfolio website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Portfolio Website
&lt;/h3&gt;

&lt;p&gt;I want to start off with this example by saying that I don't think there is a &lt;em&gt;perfect&lt;/em&gt; answer to how to do this. The best thing we can do is understand the tradeoffs we're making and do our best to make decisions that compromise the things we care about as little as possible.&lt;/p&gt;

&lt;p&gt;Let's imagine that our web site changes from having a &lt;a href="https://en.wikipedia.org/wiki/Hamburger_button"&gt;hamburger menu&lt;/a&gt; on mobile, to having a 300px wide side menu on desktop. The images on mobile will be 100% of the device width as you scroll, but on desktop, images will be 100% - 300px wide.&lt;/p&gt;

&lt;p&gt;Funny enough, we've already hit the barest possible minimum with the first paragraph of this blog post.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What size images do we need to serve at different screen sizes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start off with some assumptions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Giving a unique image size per phone screen size is beyond our abilities. So we won't have to generate a 320px wide, 375px wide, 414px wide, 640px, 828px wide etc images to satisfy every phone screen size and pixel density. We should be able to come up with a set of image sizes that covers the phone screens and pixel density fairly evenly.&lt;/li&gt;
&lt;li&gt;The website viewed on a huge screen needs a huge image, but we should come up with a sensible max size.&lt;/li&gt;
&lt;li&gt;The website viewed on a tiny screen needs a tiny image. Still keep in mind, a 414px wide screen at 3x pixel density is still 1242px wide.&lt;/li&gt;
&lt;li&gt;We have some wiggle room with showing slightly larger images because the browser can render a larger image smaller and have it look nicer than it can if it's trying to render a smaller image larger.&lt;/li&gt;
&lt;li&gt;We are not getting into art direction with the picture element in this case. We're going to render the same image at every screen size, just a different dimension and size file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Given these assumptions, we can reason about some image sizes that we think we should deliver.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;500px wide. This covers small phones rendering at single pixel density.&lt;/li&gt;
&lt;li&gt;750px wide. This covers small desktops since remember, the sidebar is 300px wide.&lt;/li&gt;
&lt;li&gt;1000px wide. This covers high density phone screens and fairly typical laptop screens.&lt;/li&gt;
&lt;li&gt;1500px wide. This will take you up to 3x pixel density phones, larger laptops and some smaller desktop monitors or people who don't fullscreen their browser.&lt;/li&gt;
&lt;li&gt;2500px wide. This is our semi-arbitrary sensible-maximum. It wont cover the 4k monitors, but if I find that screen size is popular, I can just add a key for the original.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;How do we generate the images from the original file?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like all questions, there's the robust answer for scale, and the answer for a small static site. A few years ago I had a rails site that used an image magic gem to automatically generate the right sized images based on what was uploaded. At work, we have dynamically generated and then heavily cached images from a single source file. For my wife's website, we're just going to use &lt;a href="https://imagemagick.org/index.php"&gt;ImageMagick&lt;/a&gt; because everything else is such gross overkill.&lt;/p&gt;

&lt;p&gt;I dumped all of her images into an "originals" folder so I didn't overwrite them, and then I figured that I could write some bash for loop and mess with it until it was generating all the images. But it was slow. ImageMagick has a lot of options that let you build something a little better than a bash for loop. Let's take a look at my end script.&lt;/p&gt;

&lt;p&gt;It's important to note that this is a throw away script, it works within the project structure and workflow that Emma and I have. You can pass it individual files, or loop over a glob and pss those into it, but it does just take an individual file as the parameter. You probably want to do something different for your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;PARENT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}*&lt;/span&gt;.png&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;convert &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="se"&gt;\(&lt;/span&gt; +clone &lt;span class="nt"&gt;-resize&lt;/span&gt; 2500 &lt;span class="nt"&gt;-compress&lt;/span&gt; JPEG &lt;span class="nt"&gt;-quality&lt;/span&gt; 90 &lt;span class="nt"&gt;-background&lt;/span&gt; white &lt;span class="nt"&gt;-flatten&lt;/span&gt; &lt;span class="nt"&gt;-write&lt;/span&gt; &lt;span class="nv"&gt;$PARENT_DIR&lt;/span&gt;/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-2500&lt;/span&gt;.jpg +delete &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="se"&gt;\(&lt;/span&gt; +clone &lt;span class="nt"&gt;-resize&lt;/span&gt; 1500 &lt;span class="nt"&gt;-compress&lt;/span&gt; JPEG &lt;span class="nt"&gt;-quality&lt;/span&gt; 90 &lt;span class="nt"&gt;-background&lt;/span&gt; white &lt;span class="nt"&gt;-flatten&lt;/span&gt; &lt;span class="nt"&gt;-write&lt;/span&gt; &lt;span class="nv"&gt;$PARENT_DIR&lt;/span&gt;/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-1500&lt;/span&gt;.jpg +delete &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="se"&gt;\(&lt;/span&gt; +clone &lt;span class="nt"&gt;-resize&lt;/span&gt; 1000 &lt;span class="nt"&gt;-compress&lt;/span&gt; JPEG &lt;span class="nt"&gt;-quality&lt;/span&gt; 90 &lt;span class="nt"&gt;-background&lt;/span&gt; white &lt;span class="nt"&gt;-flatten&lt;/span&gt; &lt;span class="nt"&gt;-write&lt;/span&gt; &lt;span class="nv"&gt;$PARENT_DIR&lt;/span&gt;/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-1000&lt;/span&gt;.jpg +delete &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="se"&gt;\(&lt;/span&gt; +clone &lt;span class="nt"&gt;-resize&lt;/span&gt; 750 &lt;span class="nt"&gt;-compress&lt;/span&gt; JPEG &lt;span class="nt"&gt;-quality&lt;/span&gt; 90 &lt;span class="nt"&gt;-background&lt;/span&gt; white &lt;span class="nt"&gt;-flatten&lt;/span&gt; &lt;span class="nt"&gt;-write&lt;/span&gt; &lt;span class="nv"&gt;$PARENT_DIR&lt;/span&gt;/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-750&lt;/span&gt;.jpg +delete &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="se"&gt;\(&lt;/span&gt; +clone &lt;span class="nt"&gt;-resize&lt;/span&gt; 500 &lt;span class="nt"&gt;-compress&lt;/span&gt; JPEG &lt;span class="nt"&gt;-quality&lt;/span&gt; 90 &lt;span class="nt"&gt;-background&lt;/span&gt; white &lt;span class="nt"&gt;-flatten&lt;/span&gt; &lt;span class="nt"&gt;-write&lt;/span&gt; &lt;span class="nv"&gt;$PARENT_DIR&lt;/span&gt;/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-500&lt;/span&gt;.jpg +delete &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    null:
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It loops over all the png files in a folder, takes the file, builds something out of it, outputs it a level up from the originals directory and creates a new filename off of the old one with the size appended to the file name. I'm also using JPEGs since they compress well on web and are supported in all browsers. Since it's a portfolio site, the quality I'm using is 90 since quality is important. If we cared about image quality less, we could probably get away with going down to 70.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;+clone&lt;/code&gt; clones the last option.&lt;br&gt;
&lt;code&gt;+delete&lt;/code&gt; deletes the last option.&lt;/p&gt;

&lt;p&gt;Running them at the beginning and end of each operation means we're always using the original image to generate the other sizes, so there's no chance of recursive quality degradation. the last option is &lt;code&gt;null:&lt;/code&gt;, and that's just so I can keep every line of generating the size looking the same. Normally on the last option you'd not need to clone or delete or even -write I think. The script just looks weird and is less clear without it.&lt;/p&gt;

&lt;p&gt;Now that we have our images generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we pick the best image to serve at different screen sizes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Funny enough, our browser can actually do this for us, it just needs a little guidance.&lt;/p&gt;

&lt;p&gt;HTML Image tags can have a couple of attributes aside from &lt;code&gt;src&lt;/code&gt;, we should explore how to have a single image tag select an image to show using &lt;code&gt;srcset&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's look at the &lt;a href="https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset"&gt;HTML spec for srcset&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the srcset attribute is present and has any image candidate strings using a width descriptor, the sizes attribute must also be present, and is a sizes attribute. The sizes attribute contributes the source size to the source set (if no source element was selected).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That leads us to 3 new questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is an image candidate string?&lt;/li&gt;
&lt;li&gt;What is a width descriptor?&lt;/li&gt;
&lt;li&gt;What is a sizes attribute?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can answer these very quickly by reading the spec.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;a href="https://html.spec.whatwg.org/multipage/images.html#image-candidate-string"&gt;Image candidate string&lt;/a&gt;, has a valid url, whitespace char(s), and then either a width descriptor or a pixel density descriptor. In the set, we can't have duplicate width descriptors.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://html.spec.whatwg.org/multipage/images.html#width-descriptor"&gt;Width Descriptor&lt;/a&gt; has a valid number and a "LATIN SMALL LETTER W".&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://html.spec.whatwg.org/multipage/images.html#sizes-attribute"&gt;sizes attribute&lt;/a&gt; is a source size, a source size list or a source size value. It can have media queries and values for those media queries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can put all of this together to write a pretty cool image element.&lt;/p&gt;

&lt;p&gt;First, let's generate our srcset. We'll pretend our original image was named img.jpg&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    img-500.jpg   500w,
    img-750.jpg   750w,
    img-1000.jpg 1000w,
    img-1500.jpg 1500w,
    img-2500.jpg 2500w
  "&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, for our sizes, we need a little more information. We know we have a few constraints such as the 300px static width sidebar on desktop. We know vertical tablet and under is going to be 100vw, but the static sidebars mean we sorta have to hint at conditions above those widths.&lt;/p&gt;

&lt;p&gt;Since we're giving hints and not targeting specific devices, we can actually just be fairly imprecise with our device matching, we don't have to say "Well, iPads have this... desktops start here, large desktops start here..."&lt;/p&gt;

&lt;p&gt;So for example, how much screen space will the image take up on 1200px wide? Well, it'll be 1200px - 300px = 900px. Which is 75vw.&lt;br&gt;
Let's add in a few more, on a 900px screen, the image will be 66.6...% of the width of the screen, so let's just call it an even 67%. How about another one at 1800px screen? 83vw after we do the math. Anything above that, we want to say it's close enough to 100% that we just want to load the largest image, so we just say 100vw.&lt;/p&gt;

&lt;p&gt;This can be done better, and stylistically you may want to build it from a min-width perspective than a max-width perspective. But the point is, for this specific project, as I resize the screen and look at it on different devices, the best image is served at the best time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100vw, (max-width: 900px) 67vw, (max-width: 1200px) 75vw, (max-width: 1800px) 83vw, 100vw"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we put all of it together, we've got some html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"img-500.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100vw, (max-width: 900px) 67vw,
(max-width: 1200px) 75vw, (max-width: 1800px) 83vw, 100vw"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    img-500.jpg   500w,
    img-750.jpg   750w,
    img-1000.jpg 1000w,
    img-1500.jpg 1500w,
    img-2500.jpg 2500w
  "&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A super cool image"&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"img-responsive"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And some css (this plus whatever is required to have the image on the right and the sidebar navigation on the left taking up 300px and then the mobile version):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.img-responsive&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll generate different sizes if your website is designed differently, you'll write different breakpoints and widths that images take up a those breakpoints. But without a single line of javascript, we've built an image tag that loads the right image for the right screen size.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fin
&lt;/h1&gt;

&lt;p&gt;HTML starts off accessible, it's up to us to keep it that way, and dealing with images is one of the fastest way to get off track. This hasn't covered the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture"&gt;picture element&lt;/a&gt;, which lets you do things like serve different image formats to different browsers, for example &lt;code&gt;.webp&lt;/code&gt; isn't supported by every browser yet. We've just scratched the surface of images on the web.&lt;/p&gt;

&lt;p&gt;As a developer, you can do your part by understanding your users, making good decisions based on the available information, and executing in an accessible way. If you pull it off, you can save on bandwidth, save other people on bandwidth, make your site faster and more accessible.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started With Web Workers</title>
      <dc:creator>Joshua Gilless</dc:creator>
      <pubDate>Thu, 13 Feb 2020 02:54:27 +0000</pubDate>
      <link>https://forem.com/joshuagilless/getting-started-with-web-workers-3cjb</link>
      <guid>https://forem.com/joshuagilless/getting-started-with-web-workers-3cjb</guid>
      <description>&lt;p&gt;Hello all, today I'm taking a look at web workers. The general idea is that I'd like to implement a tiny one that just sends a message back and forth between the main &lt;code&gt;index.js&lt;/code&gt; and the worker in &lt;code&gt;worker.js&lt;/code&gt;. I'm hopeful that I can show you how I explore something that I don't really know anything about. After we implement a tiny one, we'll look at further use cases for "real" ones.&lt;/p&gt;

&lt;p&gt;I start a lot of my projects the same way, just a HTML file, a JavaScript file, and a CSS file. For this project, we're not even going to need a CSS file.&lt;/p&gt;

&lt;p&gt;I have several folders at my root, personal stuff goes in a folder called "projects". When I open my terminal, I often run a few commands.&lt;/p&gt;

&lt;p&gt;Here they are, for brevity's sake, concatenated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd projects/tmp &amp;amp;&amp;amp; mkdir webworkers &amp;amp;&amp;amp; cd webworkers &amp;amp;&amp;amp; touch index.html index.js &amp;amp;&amp;amp; echo "console.log('hello');" &amp;gt; index.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It changes me to a temporary directory, where I keep a lot of little silly things that I haven't build a project out of and can delete at any time. Any time a project is in this tmp folder, it is not worth caring about. If I start to care about the project, I move it up a directory.&lt;/p&gt;

&lt;p&gt;The commands make a folder called webworkers and then adds two files, &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;index.js&lt;/code&gt;. The file &lt;code&gt;index.js&lt;/code&gt; starts off with a &lt;code&gt;console.log('hello');&lt;/code&gt; so that I can just verify that it's all connected when I first open it all up.&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://code.visualstudio.com/"&gt;VSCode&lt;/a&gt; as my editor. VSCode comes with the &lt;a href="https://emmet.io/"&gt;Emmet&lt;/a&gt; toolkit that lets you just type &lt;code&gt;html:5&lt;/code&gt;, hit tab, and it outputs a boilerplate html document.&lt;/p&gt;

&lt;p&gt;Before the closing body tag, I manually typed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And now I've got a very basic static site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple Web Server
&lt;/h3&gt;

&lt;p&gt;I use a python module to run a static web server in a directory. If you have python 2 run (in your terminal):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m SimpleHTTPServer 8000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you're using python 3 run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m http.server 8000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you're lazy, and you do this a lot, you can also add an alias to your .bash_profile or whatever file your shell executes on load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias pserve="python -m http.server 8000"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once your web server is started, open your browser, navigate to &lt;code&gt;localhost:8000&lt;/code&gt; and open the console. If you've done everything right you should see a "hello" waiting for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web Workers!
&lt;/h3&gt;

&lt;p&gt;Ok let's read a little &lt;a href="https://html.spec.whatwg.org/multipage/workers.html#worker"&gt;documentation&lt;/a&gt;. So it looks like we want to create a worker from a separate file and that worker will run on a background thread. Cool, let's make the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch worker.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It has an event handler called &lt;a href="https://html.spec.whatwg.org/multipage/workers.html#handler-worker-onmessage"&gt;onmessage&lt;/a&gt;, so we can make our whole file just that handler for now.&lt;/p&gt;

&lt;p&gt;worker.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I should explain what I'm doing here. We can use &lt;code&gt;onmessage&lt;/code&gt; directly here because in the worker context, &lt;code&gt;self&lt;/code&gt; and &lt;code&gt;this&lt;/code&gt; both point to the global worker scope. The scoping is very similar to how you may use something like &lt;code&gt;fetch&lt;/code&gt; or &lt;code&gt;console&lt;/code&gt; directly in the main thread, even though they're on the window object because the main thread's global scope is Window.&lt;/p&gt;

&lt;p&gt;If you're feeling verbose, you could write this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The advantage of using &lt;code&gt;addEventListener&lt;/code&gt; is that you don't override existing listeners on the event. If you use the sort-of-inline &lt;code&gt;onmessage&lt;/code&gt;, you only ever get one event listener at a time, and if you ever declare a new &lt;code&gt;onmessage&lt;/code&gt; handler, you override the first one. With &lt;code&gt;addEventListener&lt;/code&gt; you can add as many as you want, but you have to worry about discarding the listeners when you're done with them. It's a small tradeoff to be aware of.&lt;/p&gt;

&lt;p&gt;It's probably overall better to just stay consistent with &lt;code&gt;addEventListener&lt;/code&gt;. And if I'm using &lt;code&gt;addEventListener&lt;/code&gt;, it means I should really separate my function out in case I need to remove the listener later on. Saying out loud as I do this: "Premature optimization is the root of all evil, but building good habits is not."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ok, so that's our worker.js, let's create it and send a message.&lt;/p&gt;

&lt;p&gt;index.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Save and refresh the page and see what happens in the console.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&amp;gt; MessageEvent {isTrusted: true, data: "Hello", ...}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Very nice! There are a lot of properties on this MessageEvent object. Let's take a closer look at &lt;code&gt;data&lt;/code&gt;, since I see that has "Hello" in it, and means &lt;code&gt;data&lt;/code&gt; is what we sent.&lt;/p&gt;

&lt;p&gt;Let's try posting an object! The HTML specification says &lt;code&gt;attribute any data&lt;/code&gt;, so it can be more than a string.&lt;/p&gt;

&lt;p&gt;index.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;your&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;face&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Change what we're logging in worker.js to be just the &lt;code&gt;data&lt;/code&gt; property of the event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&amp;gt; {your: "face", is: "cool"}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well, that's what I wanted to see. Now we know how to post data to the worker, what about receiving it and bringing it back?&lt;/p&gt;

&lt;p&gt;Let's go back to &lt;code&gt;index.js&lt;/code&gt; and have our worker receive a message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;your&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;face&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And let's have our worker.js send a message when it receives one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how dare you?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Save and refresh:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&amp;gt; yes&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Excellent.&lt;/p&gt;

&lt;p&gt;Let's change &lt;code&gt;index.js&lt;/code&gt; one more time, to make sure all the data can be evaluated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;your&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;face&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not cool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Save, run again:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&amp;gt; how dare you?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There you have it, we can send messages with data back and forth from the index to the worker and back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tidying Up
&lt;/h3&gt;

&lt;p&gt;You can remove the event listeners when you're done with them, both on the worker object in the main thread, and on the global scope in the worker thread.&lt;/p&gt;

&lt;p&gt;Main thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Worker thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's an important thing to care about. Your code is probably running &lt;em&gt;somewhere&lt;/em&gt; on less expensive hardware than your 2014 MacBook Pro that you're still holding on to because you don't want one of the new terrible keyboards.&lt;/p&gt;

&lt;p&gt;Additionally, you might want to kill the worker thread when you're done with it. No use keeping it around after it's done. This can also be done in the main thread, or in the worker thread.&lt;/p&gt;

&lt;p&gt;In the main thread, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And in the worker thread, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  The Browser Check
&lt;/h3&gt;

&lt;p&gt;Ok, one last thing. Almost all &lt;a href="https://caniuse.com/#feat=webworkers"&gt;browsers support Web Workers&lt;/a&gt;, but Opera Mini exists and people still use IE6 for whatever reason. You can verify that the browser trying to run your code supports web workers with a simple check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;p&gt;Imagination is key here. This gets you some actual multithreading capabilities in the browser. It's not a JavaScript feature, JavaScript is single-threaded. This is an HTML feature.&lt;/p&gt;

&lt;p&gt;What if you put your apps state management into a background worker? You could have all your expensive state calculations run in the background and have the main thread to do all the UI work. That would be pretty sweet.&lt;/p&gt;

&lt;p&gt;What if you put all your data fetching in a worker? Your main thread wouldn't have to block anything while you're waiting on a response from the server.&lt;/p&gt;

&lt;p&gt;What if you were building some kind of data-heavy app and wanted people to be able to upload a CSV and then parse that data into your application without having to send it to a server? A Web Worker could really help with offloading file processing off of the main thread.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I think this is the simplest worker we can make. It turns out that after what we've done here, it's just writing more javascript.&lt;/p&gt;

&lt;p&gt;Each worker has a global context, but can't access the DOM, window, or document. You CAN &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers"&gt;access a few things&lt;/a&gt; that you would think of as under the normal window context that are also under the worker context.&lt;/p&gt;

&lt;p&gt;There's a lot more to explore here. I've built a tiny example, just a proof of concept that says "Yep, this thing works." The next thing to do would be to build something non-trivial, like that thing that parses a CSV file without sending it to a server.&lt;/p&gt;

&lt;p&gt;I think that &lt;a href="https://www.joshuagilless.com/exploring-magic"&gt;exploring things you don't understand&lt;/a&gt; is really important. In this case, I learned how easy web workers are to set up and use. Any time you learn something small like this, it compounds on your ability to make good decisions when you're building things later on.&lt;/p&gt;

&lt;p&gt;I hope you picked up something cool from this blog post. If you're using web workers in production, I'd love to hear about what you're using them for!&lt;/p&gt;

</description>
      <category>learning</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Fighting FUD</title>
      <dc:creator>Joshua Gilless</dc:creator>
      <pubDate>Tue, 29 Oct 2019 16:53:23 +0000</pubDate>
      <link>https://forem.com/joshuagilless/fighting-fud-eel</link>
      <guid>https://forem.com/joshuagilless/fighting-fud-eel</guid>
      <description>&lt;p&gt;On Friday evenings, I like to make cold brew coffee for the next morning. I just take a mason jar, dump in some coffee grounds and water, put the lid on and then wait for 12 hours. Saturday morning comes around, I filter it and then my wife and I go to the park and sit under a tree and have a nice coffee date. I love those early weekend mornings, there aren't a lot of people out and about. Sometimes we even get most of the park to ourselves! The deadlines from the rest of the week take a break until later, and we just have a morning to chat and sip some tasty coffee.&lt;/p&gt;

&lt;p&gt;We did this last week, and I gotta tell you, I love being alive. It's pretty cool, I have so many things that I like doing that mostly require being alive. I like being alive with my wife, I'd like to keep it going at a high quality for as long as I can. I haven't always taken the best care of myself, but I have a lot more motivation to do so these days.&lt;/p&gt;

&lt;p&gt;I just googled the average life expectancy in the US. A baby born in 2017 is expected to live to 78.7 years old. Ok, that's interesting, but now I just want to know more, how long will someone who was born in a different year live? &lt;a href="https://www.ssa.gov/oact/STATS/table4c6.html"&gt;The SSA has some actuarial tables about life expectancy&lt;/a&gt;, which is hopefully the least exciting sentence you'll read today. It looks like I can expect another 52 years of life! That's pretty good, but that means I've probably already lived over a third of my life.&lt;/p&gt;

&lt;p&gt;None of those potential remaining days are promised though. I could develop a heart anomaly, get hit by a car or murdered by an extremist. Whatever remaining days I have left, I want to make them count. I want to spend time with loved ones, read good books, eat good food, plant some trees, make cool things.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You gotta get 10,000 steps a day to stay healthy!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've had a hard time picking and sticking to an exercise/diet routine in the past. I have a tendency to try to min-max everything, which is great for software or role-playing games, but bad for a lot of other real normal life things. I've done a few things that I stuck with, P90X-3 and Couch-to-5k come to mind. After I finished P90X-3, I stagnated a little bit. I'd finished the program, I was ready to move on to something &lt;em&gt;better&lt;/em&gt;. There had to be something &lt;em&gt;better&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I spent a lot of time reading about different exercises that I could try to learn to do and add. I'm not into going to the gym or anything, so I found some beginner routines that I could work my way up the progression with. And then all these Internet People were saying that I'd never get stronger without using a barbell. So I kept reading, trying to find an exercise routine that I could do with just a couple of small dumbells and a pull-up bar.&lt;/p&gt;

&lt;p&gt;While I was doing all that reading, I wasn't exercising. I was spending so much time trying to find the &lt;em&gt;right&lt;/em&gt; routine and diet that I wasn't doing anything.&lt;/p&gt;

&lt;p&gt;The health department has a few &lt;a href="https://health.gov/paguidelines/second-edition/pdf/Physical_Activity_Guidelines_2nd_edition.pdf#page=55"&gt;recommendations for adults&lt;/a&gt;. Key takeaways are that some activity is better than none. Substantial health benefits seem to happen at 2.5 hours a week of moderate-intensity. If you want to think of it as a per-day quota: less than 30 minutes a day.&lt;/p&gt;

&lt;p&gt;The reality is, you probably don't even need 10,000 steps, &lt;a href="https://jamanetwork.com/journals/jamainternalmedicine/article-abstract/2734709"&gt;older women seem to have the mortality rate curve down to flatten at around 7,500 steps per day&lt;/a&gt;. Now, I'm not a 72 year old woman, and this study isn't about measuring quality of life, but it seems like this is one of those situations where a little effort goes a long way. Don't worry about that 10,000 number, just do a little.&lt;/p&gt;

&lt;p&gt;Instead of going for jogs or walks, I was worried that I'd hurt my knees if I wasn't doing the right routine. Instead of doing pull-ups, I worried about doing the right amount of repetitions (should I do sets of 8 or 10 or 12?). The thing is, just going for brisk walks for 30 mins a day can reduce the risk of &lt;em&gt;all-cause&lt;/em&gt; mortality. I should have been just doing &lt;em&gt;something&lt;/em&gt; instead of worrying that I was somehow not going to min-max my muscle gain.&lt;/p&gt;

&lt;h3&gt;
  
  
  FUD - Fear, Uncertainty, and Doubt
&lt;/h3&gt;

&lt;p&gt;Fear, Uncertainty, and Doubt. It's a marketing strategy used for exercise routines, advertising, public relations, propaganda, politics, and more. It came about into our English lexicon about a hundred years ago, but now you hear it a lot in conversations about software. For example, the term: "Nobody ever got fired for buying IBM." was used by companies to justify millions of dollars to purchase IBM computers. What that phrase is really doing is planting a little seed of doubt that any other vendor won't be as reliable as IBM.&lt;/p&gt;

&lt;p&gt;I had a lot of FUD around Go when I started trying to write things in it. Everything I'd read was "You just need the standard library". I'd see it everywhere, but then blog posts would often use the mux from Gorilla. The comments would all be something along the lines of "Real Go developers use the standard library." And there's a certain sort of point to that attitude, you can get REALLY far with just the standard library in Go.&lt;/p&gt;

&lt;p&gt;All of that FUD just held me back from doing cool things with Go. What I should have done was use Gorilla and replace it when I knew more and could make a more educated decision. I was struck by uncertainty into inaction. Every time I started, I would doubt myself and use that as an excuse to start over.&lt;/p&gt;

&lt;p&gt;Lots of people on twitter seem to ask about "Performance" in React. Should we write arrow functions in the Render method? Isn't that bad for performance? React and Web Components will never work, surely! They're two things that try to do the same thing differently. Right? Nope. It's a lot of FUD.&lt;/p&gt;

&lt;p&gt;Which framework should I use? Well React is owned by Facebook and they just had some data issues a few years ago, so maybe I want to try Angular? What if Google shuts down Angular like they do other projects? Ok, what about Vue? Come on, they're not even backed by a huge company, so how can I know they're going to continue to have support!? It's crazy! If you listen to questions like these, all you'll find is a lot of decision paralysis.&lt;/p&gt;

&lt;p&gt;So what if Google kills Angular? They've killed some of my favorite projects that other people also loved and used (&lt;a href="https://en.wikipedia.org/wiki/Inbox_by_Gmail"&gt;RIP, Inbox&lt;/a&gt;). I doubt that they consider Angular sacred. If they kill it, it's fine! Let's think about it: Will your application stop working? Will you still be able to write new Angular code? What if Google kills Go? The compiler is still there, you still write binaries and can write and deploy new code. How is that any better of a question than: "What if Sun kills Java?" was 10 years ago?&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of FUD
&lt;/h3&gt;

&lt;p&gt;FUD is spread in different kinds of ways. There's the common "I read that inlining a function is bad for performance..." crowd. Then there's the "Look, I used React Router four years ago and Never Again." crowd. Then there's the worst of all, those who are actively spreading disinformation to drag a "competitor's" things down. They're so different in how you deal with responding to them.&lt;/p&gt;

&lt;p&gt;The first is great, these people are parroting something they read and can learn where to put priorities. How &lt;em&gt;do&lt;/em&gt; you measure performance? Are you actually profiling? Until you can run some benchmarks and actually show the difference in memory usage or clock cycles it's not really worth worrying about. I've fallen for this kind of uncertainty time and time again, and this is the kind of thing that leads you to worry about bottlenecks that are not even close to your real bottlenecks. Developer speed, typing speed, problem-solving speed are all speeds that you probably need to worry about before you worry about performance in React.&lt;/p&gt;

&lt;p&gt;The second crowd isn't likely to be persuaded. Honestly, I don't blame anyone in this camp. If you've been burnt by something, it's hard to want to get back on the wagon. Sometimes these people do come around, but they'll do it in their own time. The best thing you can do here is to just do something kinda cool that catches their attention.&lt;/p&gt;

&lt;p&gt;The third crowd is actively doing something destructive. Microsoft viewed Linux as a competitor to Windows. Linux is used by computers and servers all over the world, and so is Windows. Back in the early 2000s, there wasn't as much knowledge available to the general public about the differences between the two operating systems. Windows ran an advertising campaign called "Get the Facts" that claimed that the TCO (Total Cost of Ownership) for Linux was as much as 10 times higher than the TCO of Windows. They even had a totally compelling graph, unless you knew the price of the hardware they were running it on. The BBC &lt;a href="http://news.bbc.co.uk/2/hi/technology/3600724.stm"&gt;covered the Get the Facts campaign&lt;/a&gt;, back in 2004. That misleading claim was intended to stir up some FUD and increase the Windows market share.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combatting Your Own FUD
&lt;/h3&gt;

&lt;p&gt;Examine your FUD. The &lt;a href="https://en.wikipedia.org/wiki/Socratic_method"&gt;Socratic method&lt;/a&gt; uses a cooperative argument of asking questions to draw out underlying assumptions. There's not necessarily a requirement that you have to have someone else to argue with though. You can ask yourself questions. You can find out where your fear, uncertainty or doubt came from.&lt;/p&gt;

&lt;p&gt;My FUD with Go was combatted by just asking myself a few questions: "What if I write this router wrong?" Well, web requests won't go where you expect. "What if I don't know about pointers and write a memory leak?" What a doozy of a question, eh? You'd have a bug and your software would crash. Eventually, I asked myself enough questions to where I just had the confidence to &lt;a href="https://www.youtube.com/watch?v=ZXsQAXx_ao0"&gt;Just Do It&lt;/a&gt;. Then I wrote and deployed software, and none of my fears were realized. It works fine.&lt;/p&gt;

&lt;p&gt;Ground yourself in reality. Trace your doubts, find out not only what you believe, but why you believe it. Perform experiments! If you can suspend your pre-judgment of something, you can usually find a way to actually perform the experiment that you think isn't going to work.&lt;/p&gt;

&lt;p&gt;Your browser has a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance"&gt;performance api&lt;/a&gt; that you can use to profile your functions. Think you should refactor your app to remove all the inline arrow functions? Measure if you should. You probably gain a lot more from correctly writing your &lt;code&gt;shouldComponentUpdate&lt;/code&gt; life-cycle functions or the comparison part of the &lt;code&gt;useEffect&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;The truth is, you'll sometimes find your fear, uncertainty or doubt is justified, but as long as it's amorphous it'll just kinda sit there, leading to wasted time worrying or wasted time stalled. Being honest with yourself is incredibly important. Being honest with yourself is a huge way to combat your own FUD. The biggest victory over FUD is not letting it interfere with you accomplishing the things you want to do.&lt;/p&gt;

&lt;p&gt;So get out there and go for a jog, use a library in Go, write a React app with inline arrow functions. The cost of doing something slightly less than 100% perfect is a lot less than the cost of doing nothing.&lt;/p&gt;

&lt;h4&gt;
  
  
  See Also
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ben/why-the-react-community-is-missing-the-point-about-web-components-1ic3"&gt;Benny Powers and Dan Abramov on React vs Web Components&lt;/a&gt;. Discussion on differences and the FUD surrounding each.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/statuscode/how-i-write-go-http-services-after-seven-years-37c208122831"&gt;Matt Ryer on structuring Go programs&lt;/a&gt;. Excellent guide that helped pull me out of Go FUD.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.merovius.de/2017/06/18/how-not-to-use-an-http-router.html"&gt;Axel Wagner on Go Routers&lt;/a&gt;. Really cool overview of what I should have done, and actually a lot closer to what I ended up with than a real router.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>react</category>
      <category>go</category>
    </item>
    <item>
      <title>Skills For The Programming Job Seeker</title>
      <dc:creator>Joshua Gilless</dc:creator>
      <pubDate>Fri, 25 Oct 2019 14:29:10 +0000</pubDate>
      <link>https://forem.com/joshuagilless/skills-for-the-programming-job-seeker-2fjf</link>
      <guid>https://forem.com/joshuagilless/skills-for-the-programming-job-seeker-2fjf</guid>
      <description>&lt;p&gt;I recently received a question from someone (paraphrased and anonymized):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've got 6 months to brush up on job skills and knowledge to get ready for the job market. I was hoping you could tell me the stack and skills that Clique uses? I want to focus on those tools and be best prepared for any openings at Clique in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a fun question, a lot of the advice out there is to "Just study algorithms and data structures". But the truth is there's a lot more to being a programmer than programming. The technology stack often doesn't matter that much, but skills matter a lot.&lt;/p&gt;

&lt;p&gt;So what &lt;em&gt;does&lt;/em&gt; Clique use?&lt;/p&gt;

&lt;p&gt;Well, a lot of everything. There's the stuff that everybody uses: Javascript, HTML, CSS, and a CDN. These technologies aren't going anywhere, the only thing that changes is how you use them. It turns out once you use them for a few years, you get used to the happy path. Doing things with them doesn't take nearly as much time as it did in the beginning. It'll take some time to get there, but continuously building things compounds learning.&lt;/p&gt;

&lt;p&gt;There's also the stuff that fewer people use: Golang, Appengine, Google Datastore. These are great technologies, but Appengine doesn't take long to get used to. Datastore is a lot like every other non-relational database. Go feels a lot like if you took Javascript, and mashed it with C. Once you've learned one language, others are easier to pick up.&lt;/p&gt;

&lt;p&gt;In my time here, I've written programs in PHP, Go, one random Ruby script, and every flavor of Javascript under the sun. Right now, we're a Javascript and Go stack. Our current favorite JS library is React, but we have an old SPA written in JQuery that we still maintain. And to be honest, most of our JQuery could be replaced by vanilla Javascript these days.&lt;/p&gt;

&lt;p&gt;So what's actually important to working here? I think I can give 5 things that I think are important to working at Clique. These skills don't go away from job to job, you'll use them everywhere you go.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have "Soft Skills"&lt;/li&gt;
&lt;li&gt;Be Able to Learn New Things Quickly&lt;/li&gt;
&lt;li&gt;Know Your Tools&lt;/li&gt;
&lt;li&gt;Have Business Skills&lt;/li&gt;
&lt;li&gt;Know Data Structures and Algorithms&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Have "Soft Skills"
&lt;/h3&gt;

&lt;p&gt;I put this at the top because it's the most important. The programming industry is small, you will meet a lot of people and you'll run into them again and again. First impressions matter, but consistent good relationships last a lot longer than jobs. "Soft skills" is buzzword that doesn't do justice to the importance of these skills. Empathy is not "soft", it's how you build relationships. Self-management is not "soft", it's how you get things done.&lt;/p&gt;

&lt;p&gt;We all have heard stories about the brilliant jerk, who writes great code, but everyone dreads interacting with. Don't be that person. Instead, be the person who can talk empathetically to everyone, from the senior managers to the people fresh out of school, in any department.&lt;/p&gt;

&lt;p&gt;Communicating technical things to non-technical people is an incredibly important part of being a programmer. Don't use jargon and do be patient. You didn't learn everything you know in a few seconds of explanation, so don't expect that of others. You're a person first and a programmer second.&lt;/p&gt;

&lt;p&gt;It's almost a guarantee that you'll screw up some times. Appreciate when people forgive you and give you room to grow, and remember to do the same for them. I haven't mastered this at the moment at all times, but I am always trying to do better. It's a good thing to learn from your mistakes and do your best to correct them in the future. Try to always treat others the way you want to be treated. This isn't something that you one day become the master of, you can always do better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be Able to Learn New Things Quickly
&lt;/h3&gt;

&lt;p&gt;Programmers are people and people don't know everything. I write a ton of code and I can get really really far without looking anything up. The longer I've been doing it the less often I have to look things up. That can change quickly, I just started learning the Go programming language. Every core programming topic I know from Javascript transfers easily, but I end up googling the standard library a lot and using the docs to figure out which method does which thing. I'm slower writing Go than I was Javascript, but I'm picking it up quickly because I have a good foundation.&lt;/p&gt;

&lt;p&gt;Part of your job will be getting new tasks of the form "Integrate with this...". They're usually quick, easy and simple. You still have to learn how to do them. Whether it's integrating your ads in with some header bidder or speeding up your server logging, you'll have to learn how to do it and then do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Know Your Tools
&lt;/h3&gt;

&lt;p&gt;You'll spend a lot of time in places you don't expect and a lot of time in places you do expect. You can predict a few tools that you need to know your way around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your text editor of choice. VS Code, Atom, Sublime, Eclipse, Jetbrains, Visual Studio, whichever. Learn it and learn it well. Get to know the shortcuts and the editor.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vim.org/"&gt;Vim&lt;/a&gt; or &lt;a href="https://www.gnu.org/software/emacs/"&gt;Emacs&lt;/a&gt;. You'll end up using them more than you think. Learn to navigate, learn to write, learn to save.&lt;/li&gt;
&lt;li&gt;The Command-Line. Linux/Mac, learn to navigate, learn to command things to do what you want. Understand &lt;a href="https://en.wikipedia.org/wiki/Unix_philosophy"&gt;the Unix Philosophy&lt;/a&gt;. I can't offer much advice on Windows, but I'd spend a lot of time learning the command line there too. Harness it, customize it, it will make you powerful.&lt;/li&gt;
&lt;li&gt;Developer Tools of your environment of choice. Browser, compiler, whatever, learn to step through code, learn to read the errors and messages.&lt;/li&gt;
&lt;li&gt;Scripting. I write tons of little scripts for things that I'll use over and over again. You get some practice in and then you write little things in a few seconds and just start using them. They can easily be discarded when you're done, but they'll save you lots of time. Scripting can be done in any language you want, some are easier than others. Check out &lt;a href="https://www.gnu.org/software/bash/"&gt;Bash&lt;/a&gt; for a good one to start with.&lt;/li&gt;
&lt;li&gt;Typing. I joke with people that I type for a living, most of my time is in front of a keyboard, and a lot of yours will be too. Practice good ergonomics, work your speed up and learn to love your keyboard.&lt;/li&gt;
&lt;li&gt;Source control. I use &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt;, there are other options and every one of them is better than none.&lt;/li&gt;
&lt;li&gt;Testing. It's worth learning a good testing philosophy. Test frameworks come and go, but learning to test the right things transcends frameworks. Testing is a tool you can use in any project to move quickly and release with confidence.&lt;/li&gt;
&lt;li&gt;Continuous Integration (CI). Worth knowing, but may not be required. You can set it up to do tests, run builds, deploy your code with zero downtime. It's great, know it at a high level and be able to learn how to set it up if you need to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's plenty more, I'm sure, if you find yourself using a tool often, learn it well. The dividends of knowing your tools add up quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Business Skills
&lt;/h3&gt;

&lt;p&gt;You're a business asset! Hopefully, you're an asset. If you're not an asset, you're probably a liability. Don't be a liability.&lt;/p&gt;

&lt;p&gt;Selling ideas, and sometimes yourself isn't really a skill that most programmers expect to need in their careers. You will have a lot of ideas that you'll have to sell people on. You'll have to negotiate timelines and priorities. To sell things, you need to be able to communicate ideas clearly and thoughtfully.&lt;/p&gt;

&lt;p&gt;Business skills aren't really taught in engineering school, but understanding the business you work for will help make you more valuable to the company you work for. Much of your value will be in writing code, but you'll find out how to write the right code by having good business skills. Find out the requirements before you start, discuss things and then build the right things together with the business.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Structures and Algorithms
&lt;/h3&gt;

&lt;p&gt;For a long time, I was sorta scared of all things Computer Science, I had a few classes in college, but there was a lot out there that just sounded intimidating. What's a red-black tree? What's a trie? When should I depth-first search, when should I breadth-first search? What's a Dijkstra?&lt;/p&gt;

&lt;p&gt;What I've found recently though is that I can understand these algorithms a lot better than I could even a few years ago. You come across a lot of random knowledge while programming. All of that knowledge builds and compounds and you end up knowing data structures and algorithms better than you expect. DOM traversal is tree traversal. &lt;code&gt;document.querySelector&lt;/code&gt; is just DFS. After you've been programming for a few years, you'll pick up more on this than you expect.&lt;/p&gt;

&lt;p&gt;Lots of people say it in the reverse order: "Algorithms and Data Structures", which is nice and alphabetical. In my opinion, algorithms often tend to show themselves based on the underlying data structures. You'll write better algorithms by understanding the data structures available, what they do and when to use them. You might not need a course on it, but it helps to at least understand the basics.&lt;/p&gt;

&lt;p&gt;I hesitated to include this section due to how broken tech-hiring is. Interviewing in the HackerRank style doesn't promote diversity, it self selects people who already have access. To pass an interview like this requires studying. Interviews that require 5 hours of commitment on-site select for people that can afford to take that off of work and sometimes afford a babysitter. Hiring like that makes &lt;em&gt;access&lt;/em&gt; one of the strongest signals.&lt;/p&gt;

&lt;p&gt;With tech hiring as bad as it is, we still are operating within the system. Plenty of companies model their hiring practices off of Google or Facebook. You can choose to not work for companies that hire with these practices, but I'm not sure that I'm one for protest votes.&lt;/p&gt;

&lt;p&gt;Why do Stanford students pass Google interviews at a higher rate than Howard students? Here's an excellent &lt;a href="https://twitter.com/mekkaokereke/status/1135980576429576192"&gt;thread on Twitter by Mekka Okereke&lt;/a&gt; that shows this better than I can. Basically, Stanford students have a &lt;a href="https://web.stanford.edu/class/cs9/"&gt;class on how to pass the interview, CS9&lt;/a&gt;. You can probably guess what happened when Howard students were given the same access. For now, we do have access to a course on Coursera, &lt;a href="https://www.coursera.org/learn/cs-tech-interview"&gt;Mastering the Software Engineering Interview&lt;/a&gt;. Which is probably a good refresher, I haven't taken it, but I would if I was interviewing in 6 months.&lt;/p&gt;

&lt;p&gt;There's also sites like &lt;a href="https://leetcode.com"&gt;LeetCode&lt;/a&gt; or &lt;a href="https://www.hackerrank.com"&gt;HackerRank&lt;/a&gt; that you can use for practice. These problems are nothing like what you'll likely end up solving at work, but if you approach them as a game, like Sudoku or Set, you can get a lot of programming practice from these. You can even solve them with a pencil and paper for bonus whiteboarding practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;If I knew I was going to be looking for a job in 6 months, I would build a few things. Put a few random little projects online. Make up your own projects. Remember that everyone builds a todo list, but just 'cause everyone does it doesn't mean you can't too! Clone a popular site, Reddit and Hacker News get cloned all the time. Clone flappy bird or some other simple game.&lt;/p&gt;

&lt;p&gt;I would spend time reading. Read up on anything software related. Learn cool things, like graph traversal, linear algebra, geometry. Learn random things that you'll probably never use like how to turn on your webcam with OCaml or how to animate a bouncing ball in CSS. Every piece of knowledge you gain compounds and you'll eventually end up knowing more than you even knew was possible when you first started.&lt;/p&gt;

&lt;p&gt;I would spend some time writing my resume. I'd pick what I think needs to be on my resume, learn it, build a project with it and then put it on my resume. It might even be good to keep your LinkedIn up to date.&lt;/p&gt;

&lt;p&gt;I would study data structures and algorithms. I would join a site like &lt;a href="https://leetcode.com/"&gt;LeetCode&lt;/a&gt; or &lt;a href="https://www.hackerrank.com/"&gt;HackerRank&lt;/a&gt; and pick a language. If the jobs I wanted to go for were backend, I'd pick something like Go or Python, if the jobs were for frontend, I'd pick Javascript/Node. Then I'd do lots of problems in that language.&lt;/p&gt;

&lt;p&gt;When the time came, I'd apply, I'd go in with some confidence that I have the basics and can learn what I need to.&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>Shave That Yak!</title>
      <dc:creator>Joshua Gilless</dc:creator>
      <pubDate>Wed, 28 Aug 2019 01:54:04 +0000</pubDate>
      <link>https://forem.com/joshuagilless/shave-that-yak-484</link>
      <guid>https://forem.com/joshuagilless/shave-that-yak-484</guid>
      <description>&lt;p&gt;One of my all-time favorite expressions is "Yak Shaving". It makes me laugh. It's super visual to imagine myself out there with the same electric clippers I use on my beard just kinda trying to get the hair off of this huge bovine. Truth be told, I'm not even sure I've seen a yak outside of a documentary, so I'm just imagining a big cow that has beautiful long flowing hair (cow Rapunzel?).&lt;/p&gt;

&lt;p&gt;The origin of the phrase is someone named Carlin Vieri who was at MIT at the time. An &lt;a href="http://projects.csail.mit.edu/gsb/old-archive/gsb-archive/gsb2000-02-11"&gt;email&lt;/a&gt; from Jeremy H. Brown explains:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You see, yak shaving is what you are doing when you're doing some stupid, fiddly little task that bears no obvious relationship to what you're supposed to be working on, but yet a chain of twelve causal relations links what you're doing to the original meta-task.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some people, like Seth Godin, argue against it on his blog post &lt;a href="https://seths.blog/2005/03/dont_shave_that/"&gt;"Don't Shave That Yak"&lt;/a&gt;. Others, like Florian Gilcher, point out that &lt;a href="https://yakshav.es/the-patron-saint-of-yakshaves/"&gt;good things come from shaving yaks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.joshuagilless.com"&gt;My blog&lt;/a&gt; itself is a yakshave. I decided that I wanted to write a blog post. Then I decided that I didn't want to use Wordpress or Drupal and everything went downhill from there. I decided that it had to be an application of my own design. I wrote some blog software a long time ago in Ruby after I watched &lt;a href="https://twitter.com/dhh"&gt;DHH's&lt;/a&gt; famous build a blog in 15 minutes demo. But I didn't want to write a blog in Ruby, I'd done that before. At work, we were using this cool language called Go.&lt;/p&gt;

&lt;p&gt;I really enjoyed Go. I kept making little toy apps that would connect to a database, or validate JWTs, or muck around with general internet stuff. I built a small web crawler to do SEO audits, I built a little thing to handle OAuth to Google with and then persist it with client-side JWT's. None of it was really coming together into blogging software though.&lt;/p&gt;

&lt;p&gt;Every time I tried to get started writing the blog software, I would look around for learning resources on routing in Go. I constantly saw people say things like "You just need the standard library". So I started a few times and came up with a few small routers using the standard library, and they were at best just ok. I didn't like how I ended up using them, so I kept falling back to using Gorilla or Echo.&lt;/p&gt;

&lt;p&gt;Anyway, I decided to start writing the software at the bottom. Since I was already using Go, I looked at &lt;a href="https://cloud.google.com/appengine/"&gt;AppEngine&lt;/a&gt; - both are made by Google, so they &lt;em&gt;had&lt;/em&gt; to go together. I knew that I wanted a way to write the posts, and that meant I needed a way to persist the data, a database. I hadn't used a NoSQL DB before, so I thought I'd try NoSQL. Google Cloud has a NoSQL DB called Datastore, but it was getting replaced by one called &lt;a href="https://cloud.google.com/firestore/"&gt;Firestore&lt;/a&gt; as I started this project, so I figured, let's just use Firestore.&lt;/p&gt;

&lt;p&gt;I read the docs and got up and running pretty fast, but I knew that I wanted a way to post data to Firestore. So I started writing a small CMS to just be able to log in and post stuff. Firestore led me to &lt;a href="https://firebase.google.com/"&gt;Firebase&lt;/a&gt;, and I ended up with Firebase Auth kinda out of nowhere because I had decided to use Firestore. It worked great! I even thought about just making the whole application that SPA and displaying posts through the same program.&lt;/p&gt;

&lt;p&gt;Back to Go. I wrote a server-side application that interfaces with Firestore. It uses basic IAM management to secure it. It doesn't include Firebase's JS because that bundle is large and I didn't want to. I also wrote a router in Go using the standard library. It's not great and I wouldn't recommend other people use it. On the other hand, I'm really happy that I dove in and wrote enough server code to get something working.&lt;/p&gt;

&lt;p&gt;Writing your own router in a language you don't really know is very different than writing a blog post. There's a lot of cool stuff in these seemingly unrelated tasks that I learned because I was willing to follow the yakshave until I got what I wanted. I wrote a CMS, I wrote a router, I wrote a bunch of templates, all so I can shout into the ether.&lt;/p&gt;

&lt;p&gt;Yak Shaving happens in real life too, not just in programming worlds. Sometimes you end up following a chain of tasks that don't seem related at all to what you should be doing. But if you're learning from those tasks and can build back up to what you should be doing, then those tasks are worth doing.&lt;/p&gt;

&lt;p&gt;Instead of just saying "Don't Shave That Yak!", figure out if there's something worthwhile to learn from following the chain until you do have to shave the yak. Because sometimes shaving the yak is the right thing to do.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How To Secure Your AWS S3 Bucket With Cloudflare</title>
      <dc:creator>Joshua Gilless</dc:creator>
      <pubDate>Fri, 09 Aug 2019 16:32:45 +0000</pubDate>
      <link>https://forem.com/joshuagilless/how-to-secure-your-aws-s3-bucket-with-cloudflare-4m14</link>
      <guid>https://forem.com/joshuagilless/how-to-secure-your-aws-s3-bucket-with-cloudflare-4m14</guid>
      <description>&lt;p&gt;I've noticed a lot of people are hosting their website on &lt;a href="https://aws.amazon.com/s3/"&gt;AWS S3&lt;/a&gt;, with &lt;a href="https://www.cloudflare.com/"&gt;Cloudflare&lt;/a&gt; in front of it. It's a great option because it's cheap and easy to maintain for a single developer. A static site is fairly secure by default due to the small &lt;a href="https://en.wikipedia.org/wiki/Attack_surface"&gt;attack surface&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;S3 lets you host websites from the bucket, with the condition that your bucket name is the same as the URL of the site you're hosting. For example, my website is &lt;a href="https://www.joshuagilless.com"&gt;https://www.joshuagilless.com&lt;/a&gt;, so to host a website at that location, the s3 bucket would be &lt;code&gt;www.joshuagilless.com&lt;/code&gt;. This means that everything you put in the bucket has a &lt;em&gt;predictable URL pattern&lt;/em&gt;. They have a fantastic &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/HostingWebsiteOnS3Setup.html"&gt;guide for setting up static website properties&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In that guide, you're instructed to give your bucket &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/read-access-objects-s3-bucket/"&gt;&lt;em&gt;public read access&lt;/em&gt;&lt;/a&gt;. That means that with &lt;em&gt;public read access&lt;/em&gt; and a &lt;em&gt;predictable URL pattern&lt;/em&gt;, any old joker off the street can go around your website and directly to your s3 bucket. The thing is, if you go on to the next &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html"&gt;guide&lt;/a&gt; in the series, setting your bucket up with a custom domain, it still instructs you to put a &lt;em&gt;public read access&lt;/em&gt; bucket policy.&lt;/p&gt;

&lt;p&gt;Next, in the Amazon guide series, you have &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-cloudfront-walkthrough.html"&gt;instructions&lt;/a&gt; to add &lt;a href="https://aws.amazon.com/cloudfront/"&gt;AWS Cloudfront&lt;/a&gt;. Cloudfront is a CDN and an excellent one at that. All of the tips in this post would apply to Cloudfront as well as Cloudflare. The thing about the instructions, they don't mention removing the &lt;em&gt;public read access&lt;/em&gt;. So even the official guide is missing this piece of the security puzzle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Locking down the ACL (Access Control List) gives us all of the protections that Cloudflare promises.&lt;/p&gt;

&lt;p&gt;Imagine a scenario where someone decides they want to cause you a large hosting bill and just start requesting objects directly from your S3 bucket. They could find the largest file you host and just request it over and over and over until you started getting a hefty bandwidth bill from AWS. Cloudflare has DDOS protection that would mitigate this.&lt;/p&gt;

&lt;p&gt;So how do you do it?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload things without allowing public read access.&lt;/li&gt;
&lt;li&gt;Remove the overall bucket rule allowing everything public read access.&lt;/li&gt;
&lt;li&gt;Create a bucket policy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The default uploading strategy is to not allow the public-read-access, but a lot of guides have mentioned overriding it so that you can access the files from a handy dandy URL. It's important to not override this so that when you try to access it, you get a 403 Forbidden error.  If you're making a new bucket, just go with the defaults.&lt;/p&gt;

&lt;p&gt;To create a bucket policy, AWS Gives you a &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-bucket-policy.html"&gt;walkthrough on adding bucket policies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So what we need to do is give it a statement for PublicReadGetObject:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::&amp;lt;your-domain&amp;gt;/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        list,
                        of,
                        allowed,
                        ip,
                        addresses
                    ]
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are two things you need to replace here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What's between the "aws:SourceIp" brackets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;your-domain&amp;gt;&lt;/code&gt; with your actual domain name. For example, I would replace &lt;code&gt;&amp;lt;your-domain&amp;gt;&lt;/code&gt; with &lt;code&gt;www.joshuagilless.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For figuring out the updated list for Cloudflare, you can visit &lt;a href="https://www.cloudflare.com/ips-v4"&gt;https://www.cloudflare.com/ips-v4&lt;/a&gt; and &lt;a href="https://www.cloudflare.com/ips-v6"&gt;https://www.cloudflare.com/ips-v6&lt;/a&gt; and add those lists.&lt;/p&gt;

&lt;p&gt;I learned that we can mix IPv4 and IPv6 addresses from &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev//example-bucket-policies.html#example-bucket-policies-use-case-3"&gt;the amazon docs on bucket policies&lt;/a&gt; in the section titled: "Allowing IPv4 and IPv6 Addresses". It doesn't matter which order you put them, just as long as they're formatted correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Just Give Me Something to Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::&amp;lt;your-domain&amp;gt;/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "173.245.48.0/20",
                        "103.21.244.0/22",
                        "103.22.200.0/22",
                        "103.31.4.0/22",
                        "141.101.64.0/18",
                        "108.162.192.0/18",
                        "190.93.240.0/20",
                        "188.114.96.0/20",
                        "197.234.240.0/22",
                        "198.41.128.0/17",
                        "162.158.0.0/15",
                        "104.16.0.0/12",
                        "172.64.0.0/13",
                        "131.0.72.0/22",
                        "2400:cb00::/32",
                        "2606:4700::/32",
                        "2803:f800::/32",
                        "2405:b500::/32",
                        "2405:8100::/32",
                        "2a06:98c0::/29",
                        "2c0f:f248::/32"
                    ]
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And again, just replace &lt;code&gt;&amp;lt;your-domain&amp;gt;&lt;/code&gt; with your actual bucket name and website URL.&lt;/p&gt;

&lt;p&gt;Any time you're using a CDN in production, you can do something similar to this to limit access to your locations that you don't want exposed. The same concept of only allowing a CDN's IP addresses applies to all resources, not just the ones in an S3 bucket.&lt;/p&gt;

&lt;p&gt;I hope you find this useful, it took me a while to realize that I should be doing this, so I left my bucket unsecured for way too long. Nobody likes an unsecured bucket.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>aws</category>
      <category>security</category>
    </item>
  </channel>
</rss>
