Curl Noise, Demystified

tutorial

In my recent post on mapping Perlin noise to angles, I was put on to the subject of Curl noise, which I thought I understood, but did not. I figured out what Curl noise really was in a subsequent post and then posted my earlier incorrect (but still interesting and perhaps useful) concept of Curl noise in yet another post. Although I kind of understood what Curl noise was at that point, I wanted to give myself a more complete understanding, which I usually do by digging into the code, making sure I understand every line 100% and seeing what else I can do with it, trying to make multiple visualizations with it to test my understanding, etc.

SVG Filters in Canvas

tutorial

Earlier I talked about the new (and still experimental) filters that you can apply to the HTML Canvas’s 2d rendering context. One of the more powerful aspects of this is the ability to use external SVG filters in this workflow.

External SVG filters are applied using the url filter. It looks like this:

context.filter = "url(pathtofilter)";

It took quite a bit of frustration to figure out how to actually get these to work though. The documentation says that the path should point to an external XML document that contains an SVG filter. Again, all of this was borrowed from the CSS filter that works the same way. So let’s get an example of that working first.

SVG Filters in CSS

For this example, we’ll create a div and give it a style. The style will load in an external SVG document that has a filter defined in it. We’ll use the turbulence filter, which I believe uses some version of Perlin noise.

First the HTML:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      #filtered {
        filter: url(./filter.svg#turb);
        width: 600px;
        height: 600px;
      }
    </style>
  </head>
  <body>
    <div id="filtered"></div>
  </body>
</html>

The div is down at the bottom with an id of “filtered”. The style block creates the following filter:

filter: url(./filter.svg#turb);

This points to a file named filter.svg and a specific filter within that document with an id of turb. Next, lets look at that SVG document.

<svg
  version="1.1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  >
  <defs>
    <filter id="turb">
      <feTurbulence baseFrequency="0.01"/>
    </filter>
  </defs>
</svg>

This is a pretty bare bones SVG document. You see the filter there with the id of turb. This creates a turbulence filter with the line:

<feTurbulence baseFrequency="0.01"/>

That’s all. Run it and you get:

Now let’s see if we can do the same thing in a Canvas.

SVG Filters in Canvas

Per the documentation, we should just be able to do the same thing to load in an SVG filter and apply it to a 2d rendering context:

context.filter = "url(./filter.svg#turb)";

So let’s try it out. We’ll create a canvas right in the HTML doc:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <canvas id="canvas" width="600" height="600"></canvas>
    <script src="./main.js"></script>
  </body>
</html>

And in that script, we’ll get a reference to the canvas and context and try to apply the filter.

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

context.filter = "url(./filter.svg#turb)";
context.fillRect(0, 0, 100, 100);

When I run this, I get the black square that I drew, but no turbulence filter.

After a lot of digging in, I discovered that the context filter for external SVG docs does not seem to work in the same way. There is a workaround though – you add the SVG directly to the HTML of the main page. Our HTML now looks like this:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
  <svg
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    >
  <defs>
    <filter id="turb">
      <feTurbulence baseFrequency="0.01"/>
    </filter>
  </defs>
</svg>
    <canvas id="canvas" width="600" height="600"></canvas>
    <script src="./main.js"></script>
  </body>
</html>

Now the script can load the filter directly by its id:

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

context.filter = "url(#turb)";

context.fillRect(0, 0, 100, 100);

And now we have some turbulence in our context!

But notice how it’s shifted over to the right. That’s because the SVG is now on the page. Even though it has no content, it’s taking up space. You can see it using the dev tools to highlight it:

There are probably a number of ways to handle this with CSS. Here’s how I dealt with it:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
  <svg
    style="width: 0; height: 0; position: absolute;"
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    >
  <defs>
    <filter id="turb">
      <feTurbulence baseFrequency="0.01"/>
    </filter>
  </defs>
</svg>
    <canvas id="canvas" width="600" height="600"></canvas>
    <script src="./main.js"></script>
  </body>
</html>

This makes the width and height 0 and the absolute position makes sure it doesn’t affect the layout of anything around it. Problem solved.

More Weirdness

Note that I’m drawing a 100×100 black box into the context, but the turbulence filter just fills the entire canvas with its Perliny swirls. It seems that the black box is kind of useless. But if you remove it, you get … a blank canvas. So it seems the filter is applied only when something is drawn. In fact, it doesn’t matter what you draw. A single pixel will activate the filter and give you full canvas turbulence.

But then it gets more weirder. EVERY TIME you draw something, the filter is applied again. Trying this…

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

context.filter = "url(#turb)";

context.fillRect(0, 0, 100, 100);
context.fillRect(0, 0, 100, 100);
context.fillRect(0, 0, 100, 100);
context.fillRect(0, 0, 100, 100);
context.fillRect(0, 0, 100, 100);

Gives you this…

It applied the filter 5 times, making it a lot darker.

So if you want to apply a filter like this and then draw something on top of it, you have to kill the filter. This would seem pretty easy. But the first few things I tried did not work so well.

context.filter = "";
context.filter = null;
context.filter = "null";   // worth a shot!

None of these did anything. The previous filter remained active. Finally I found that this worked:

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

context.filter = "url(#turb)";

context.fillRect(0, 0, 100, 100);

context.filter = "url()";
context.fillRect(0, 0, 100, 100);

Applying an actual filter type with no parameters does the trick. This also works:

context.filter = "blur()";

There might be a better way to do this. I stopped when I found something that worked.

Summary

Applying SVG filters to a context is pretty powerful. I’ll be checking out other available filter types. Here’s a list:

  • <feBlend>
  • <feColorMatrix>
  • <feComponentTransfer>
  • <feComposite>
  • <feConvolveMatrix>
  • <feDiffuseLighting>
  • <feDisplacementMap>
  • <feFlood>
  • <feGaussianBlur>
  • <feImage>
  • <feMerge>
  • <feMorphology>
  • <feOffset>
  • <feSpecularLighting>
  • <feTile>
  • <feTurbulence>
  • <feDistantLight>
  • <fePointLight>
  • <feSpotLight>

And you can combine several of these filters into your own custom filter that you apply to a canvas. It gets really powerful. I’ll probably be playing around with this more in the near future and will share my results.

At the same time, the workflow for creating and applying filters to a canvas rendering context is still pretty janky. I think the CSS flow is pretty solid, but as I said in my last post, the canvas feature is still experimental.

Resources

If you want to find out more about creating and combining SVG filters into neat patterns, here are some links:

https://css-tricks.com/creating-patterns-with-svg-filters/

https://www.linkedin.com/pulse/26-images-you-wont-believe-were-created-only-svg-bence-szab%C3%B3/

https://www.smashingmagazine.com/2015/05/why-the-svg-filter-is-awesome/

All of these techniques should work pretty well in native SVG elements as well as CSS, and theoretically in canvas too, with a bit of effort.

New HTML Canvas Stuff: Filters

tutorial

For the past couple of years or so, I’ve focused mainly on creating images and animations with Go and my own Go library, blgo, that uses CairoGraphics bindings (my own fork of go-cairo).

When I started creating MiniComps, it was an opportunity to get back into JavaScript-based content creation using HTML’s Canvas and its 2d rendering context. The canvas drawing API is pretty much the same as Cairo, but it’s been nice getting back into interactive, web-based pieces – not so easy in Go/Cairo. I made an updated version of my bitlib library for 2021, bljs, and if you’ve been on this blog or follow me on Twitter, you’ve seen some of the stuff I’ve been doing with it.

But recently I started looking at the documentation for Canvas and the 2d rendering context and found some pretty interesting things that weren’t there last time I looked.

Filters!

This is super powerful. Filters are a cool new way to change the look of your canvas drawings. The context now has a filter property, which is a string and allows you to add one or more defined filters to it. For example, to add a blur:

context.filter = "blur(8px)";

To add multiple filters, just add them to the string:

context.filter = "blur(8px) contrast(50%) sepia(20%)";

Here’s a demo that allows you to play with a handful of the filters available:

Source

Here are the full list of filters available:

  • blur
  • brightness
  • contrast
  • drop-shadow
  • grayscale
  • hue-rotate
  • invert
  • opacity
  • saturate
  • sepia

If you look through these, you’ll realize these are basically the same as already existing CSS filters. In fact, you could already do some of these same affects by adding a filter style to the canvas element itself. For example:

canvas.style = "filter: invert(100%)";

Here’s another demo using this method:

Source

So what’s the difference?

Actually, a pretty big difference. When you apply a filter to a 2d rendering context, it does NOT change the existing content. It will only affect anything drawn after the point when the filter was applied. If you take a look at the code in the first example, I need to clear the canvas and redraw all the content in order to have the filter take place. Whereas applying the filter to the canvas affects the whole element in real time. No need to redraw the content. This also means that using the CSS filter on the canvas element is non-destructive. Using a blur, for example, on the context means the subsequently drawn pixels are permanently blurred.

Also, because the CSS filters apply to the whole element, the blur filter will blur the element beyond its boundaries, whereas the context filter only applies to the pixels inside the canvas. (You can probably change that for the CSS version by applying other styles, such as overflow.)

So far it seems like the CSS version has a lot of the advantages. But the context version does have some plus points. If you want to export the blurred or otherwise filtered image and save it somewhere, you’ll need to have the filters applied to the image in the context, not just the canvas element.

Also, applying filters to the context means you can selectively turn them off and on or change their parameters for each shape you draw. Here’s an example:

Source

Here, the red square is drawn with no filter, the black one with a 10px blur, and the blue with a 0px blur. You couldn’t do that directly with a CSS filter.

Experimental

It’s worth noting that these are experimental features. They may not work in all browsers. Definitely WON’T work as of this writing in Safari or IE, and may change in the future.

SVG Filters

One of the more powerful aspects of being able to use filters is that you are not limited to the fairly basic assortment of built-in filters. You can create extremely complex and powerful filters using SVG and then apply those directly to your canvas context. I’ll discuss how to do that in another article.

Resources

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter

https://developer.mozilla.org/en-US/docs/Web/CSS/filter

Yet Another Way to Draw Lines

tutorial

Sure, why not, right?

One of the things I was trying to accomplish with the shaky drawing from my last post was to give the idea of hand-drawn lines. By adding in a bit of random variance, lines and shapes can seem like they are drawn by a human rather than perfectly rendered by a computer.

But at some point I was actually sketching something by hand and realized another big difference between computer drawing and human sketching. Very often when a person is sketching, rather than boldly drawing what they hope is a straight line from point A to B, they will instead make a number of very light lines that roughly go between the two points. You’ve seen it, you’ve done it, you know what I mean. So let’s replicate that.

The idea is that when we want to draw a line from x0, y0 to x1, y1 for example, we’ll actually draw multiple lines – not exactly to those to points, but from somewhere around the first point, to somewhere around the second one.

In this case, I’m going to dive right into a function. This was my first attempt:

function sketchLine(context, x0, y0, x1, y1, count, rand) {
  context.beginPath();
  for (let i = 0; i < count; i++) {
    let x = x0 + Math.random() * rand - rand / 2;
    let y = y0 + Math.random() * rand - rand / 2;
    context.moveTo(x, y);

    x = x1 + Math.random() * rand - rand / 2;
    y = y1 + Math.random() * rand - rand / 2;
    context.lineTo(x, y);
  }
  context.stroke();
}

We can then call this like so:

context.lineWidth = 0.2;
sketchLine(context, 100, 100, 700, 700, 5, 20);

Note that I set the line width down to 0.2 to make very light lines. We’ll let the multiple light lines build up to create the idea of a sketch, the same way you would do it by hand. So this will draw 5 lines with a variance of up to 10 pixels in any direction from the starting and ending points. The result:

Not that great, to be honest. The lines vary a bit, but they are still too uniform. I can bump up the randomness to, say, 50, but that looks even less like what a real person would sketch.

The thing with sketching, particularly in longer lines, is that you don’t always sketch the entire length of the line on each stroke. You might start around the first point and stroke maybe half way to the other point. And then you might stroke the middle third of the distance between them, and then something closer to the ending point. You’d do a bunch of strokes with varying start and end distances. Well, we can do something like that too.

function sketchLine(context, x0, y0, x1, y1, count, rand) {
  const dx = x1 - x0;
  const dy = y1 - y0;

  context.beginPath();
  for (let i = 0; i < count; i++) {
    let t0 = Math.random() * 0.5;
    let t1 = Math.random() * 0.5 + 0.5;
    
    let x = x0 + dx * t0 + Math.random() * rand - rand / 2;
    let y = y0 + dy * t0 + Math.random() * rand - rand / 2;
    context.moveTo(x, y);

    x = x0 + dx * t1 + Math.random() * rand - rand / 2;
    y = y0 + dy * t1 + Math.random() * rand - rand / 2;
    context.lineTo(x, y);
  }
  context.stroke();
}

This should look somewhat familiar if you’ve followed along with the other two posts in this series. First I get length of the line on the x and y axes – dx and dy. In the loop, I create a t value between 0 and 1. In this case, I create two of these – t0 and t1 – one for the start of the line and one for the end. t0 will range from 0.0 to 0.5 and t1 will go from 0.5 to 1.0. This should give us a random assortment of lines – some from very near the start point to very near the end point, some more in the first part of the line, some in the middle and some more near the end. Calling this again with:

context.lineWidth = 0.2;
sketchLine(context, 100, 100, 700, 700, 5, 20);

We get this image:

That’s something I could almost start to believe was done by hand. Let’s do a rectangle function!

function sketchRect(context, x, y, w, h, count, rand) {
  sketchLine(context, x, y, x + w, y, count, rand);
  sketchLine(context, x + w, y, x + w, y + h, count, rand);
  sketchLine(context, x + w, y + h, x, y + h, count, rand);
  sketchLine(context, x, y + h, x, y, count, rand);
}

Obviously, I just copied and pasted the shakyRect from the previous post and changed the method names and parameters. So let’s call this with:

context.lineWidth = 0.2;
sketchRect(context, 100, 100, 600, 600, 5, 20);

Pretty cool, but this brings up an issue we couldn’t immediately see when drawing a single line. All too often, the lines don’t reach all the way to those starting or ending points. Too many are falling right in the middle. We can shift our random parameters a bit to make up for that.

function sketchLine(context, x0, y0, x1, y1, count, rand) {
  const dx = x1 - x0;
  const dy = y1 - y0;

  context.beginPath();
  for (let i = 0; i < count; i++) {
    let t0 = Math.random() * 0.4 - 0.1;
    let t1 = Math.random() * 0.4 + 0.7;
    
    let x = x0 + dx * t0 + Math.random() * rand - rand / 2;
    let y = y0 + dy * t0 + Math.random() * rand - rand / 2;
    context.moveTo(x, y);

    x = x0 + dx * t1 + Math.random() * rand - rand / 2;
    y = y0 + dy * t1 + Math.random() * rand - rand / 2;
    context.lineTo(x, y);
  }
  context.stroke();
}

Now, t0 will range from -0.1 to +0.3, and t1 will go from 0.7 to 1.1. This makes it more likely that some lines will make it closer to one of the points, and may even overshoot it a bit, which is just what you’d do by hand. The results of this change:

Not bad, in my opinion. You’ll still have some rectangles with open corners, but not as often as before. Here’s one with a count of 20 and a rand of 40:

Finally, just for the heck of it, I combined the shaky and sketchy techniques with this function:

function sketchLine(context, x0, y0, x1, y1, count, rand) {
  const dx = x1 - x0;
  const dy = y1 - y0;

  context.beginPath();
  for (let i = 0; i < count; i++) {
    let t0 = Math.random() * 0.4 - 0.1;
    let t1 = Math.random() * 0.4 + 0.7;
    
    let xA = x0 + dx * t0 + Math.random() * rand - rand / 2;
    let yA = y0 + dy * t0 + Math.random() * rand - rand / 2;
    let xB = x0 + dx * t1 + Math.random() * rand - rand / 2;
    let yB = y0 + dy * t1 + Math.random() * rand - rand / 2;
    shakyLine(context, xA, yA, xB, yB, 20, 2);
  }
  context.stroke();
}

Here, I calculated the start and end points of each sketched line and rather than using moveTo and lineTo I called the shakyLine function from my last post. I hard coded it with a res of 20 and a rand of 2, which makes it just a little bit less than a perfectly straight line and possibly a little bit more like something hand drawn. Called with:

context.lineWidth = 0.2;
sketchRect(context, 100, 100, 600, 600, 10, 20);

it gives us:

Even that might be too much shake, but you can easily make it more subtle.

Here’s one final example. If this doesn’t look hand sketched, I don’t know what does.

Well, that’s that. I don’t have a ready-made library to share with you on this one, but hopefully you’ll find this useful or at least inspiring.

The rest of the How to Draw a Line series:

1. How to Draw a Line

2. More Ways to Draw a Line

3. Yet Another Way to Draw Lines

More Ways to Draw Lines

tutorial

In the last post, I discussed an alternate way of rendering lines, that could give a glitchy look, painterly look, or many other looks based on what parameters you used. In this post, I’ll discuss another way to draw lines.

The theme to these posts is that using moveTo / drawTo is fine but gives you clinical, antiseptic, perfect straight lines (or curves). And sometimes it’s nice to shake things up a bit. In fact, shaking things up is exactly what we’re going to do here today.

I’m going to start with something very similar to one of the earlier examples in the last post, but instead of drawing individual pixels along the length of the line, I’m going to draw short line segments.

const dx = x1 - x0;
const dy = y1 - y0;
const dist = Math.sqrt(dx * dx + dy * dy);
const res = 10;

context.beginPath();
context.moveTo(x0, y0);

for (let i = res; i < dist; i += res) {
  let t = i / dist;
  let x = x0 + dx * t;
  let y = y0 + dy * t;
  context.lineTo(x, y);
}
context.lineTo(x1, y1);
context.stroke()

Like before, I’m starting with two points defined by x0, y0 and x1, y1. And I’ve defined a res variable that dictates the length of those intermediate segments. Note that I moveTo the first point and set i equal to res to initialize the loop, and then lineTo the last point. This ensures that the start and end of the line remain constant (which will be important later). Otherwise, things should be familiar here if you read the previous post. Here’s what this gives us:

It’s exactly what you’d get if you just drew a line from the first to last point. So big deal. But now that we have these intermediate points, we can shake things up as promised.

I’ll just randomly shift each x, y point a bit before drawing to it. (Just showing the for loop here:

for (let i = res; i < dist; i += res) {
  let t = i / dist;
  let x = x0 + dx * t;
  let y = y0 + dy * t;
  x += Math.random() * 4 - 2;
  y += Math.random() * 4 - 2;
  context.lineTo(x, y);
}

This puts each x, y location somewhere from -2 to +2 on each axis. And gives us this:

I then put this all into a function to make it easily reusable:

function shakyLine(context, x0, y0, x1, y1, res, rand) {
  const dx = x1 - x0;
  const dy = y1 - y0;
  const dist = Math.sqrt(dx * dx + dy * dy);

  context.beginPath();
  context.moveTo(x0, y0);

  for (let i = res; i < dist; i += res) {
    let t = i / dist;
    let x = x0 + dx * t;
    let y = y0 + dy * t;
    x += Math.random() * rand - rand / 2;
    y += Math.random() * rand - rand / 2;
    context.lineTo(x, y);
  }
  context.lineTo(x1, y1);
  context.stroke();
}

Now I can just call it like so:

shakyLine(context, 100, 100, 700, 700, 10, 10);

And it creates a line like this:

Note that the random factor is 10 here, so it’s a lot more shaky than the earlier example. Note that the res and rand parameters both contribute in different ways to the shakiness of the line. So if I move res down to 5, but keep rand at 10, we get something more shaky, but in a tight way.

But here, I’ve bumped both res and rand up significantly:

shakyLine(context, 100, 100, 700, 700, 50, 40);

This gives us a line that varies a lot more, but because each segment is longer, it’s more of a chunky random shake.

So you can play these two parameters off each other to get various effects.

The next thing to do is create a shakyRect function. It looks like this:

function shakyRect(context, x, y, w, h, res, rand) {
  shakyLine(context, x, y, x + w, y, res, rand);
  shakyLine(context, x + w, y, x + w, y + h, res, rand);
  shakyLine(context, x + w, y + h, x, y + h, res, rand);
  shakyLine(context, x, y + h, x, y, res, rand);
}

As mentioned before, the fact that each line starts and ends on a non-shaky point means that the rectangle will be continuous. You can call it like this:

shakyRect(context, 100, 100, 600, 600, 10, 10);

And get a rectangle like this:

To show how res and rand relate, I made this for loop that draws a bunch of squares on the canvas. res increases from left to right and rand increases from top to bottom. It gives you a good idea of the different effects you can create.

for (let y = 20; y < 730; y += 70) {
  for (let x = 20; x < 730; x += 70) {
    shakyRect(context, x, y, 50, 50, x/730 * 10, y/730 * 10);
  }
}

Now, this rectangle function isn’t the best because it doesn’t support fills, but you can probably work up a better solution. You might also want to create functions for drawing other shaky shapes.

Or, maybe you just want a drop in library you can use. I created a whole JavaScript shaky library about 7 years ago. I can’t guarantee how well it has stood the test of time. There are undoubtedly some best practices in there that could be updated. But the core code should be good enough to work out things for yourself. It supports not only lines and rectangles, but circles, arcs, quadratic curves, bezier curves, and arcs. And as of this writing, it has 101 stars, which is kind of neat!

https://github.com/bit101/shaky

All the posts in the How to Draw a Line series:

1. How to Draw a Line

2. More Ways to Draw a Line

3. Yet Another Way to Draw Lines

How to Draw a Line

tutorial

… in code of course.

First, I’m going to assume that you are working in some kind of system that has a drawing API. But wait, even the simplest of drawing APIs have a function to draw a line already. All right then, we’ll start with that. Assuming you are using something like HTML, JS and Canvas, you’re going to do something like this:

context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.stroke();

You start with two points, defined by two x, y coordinates. You move to the first one, you line to the second one, you stroke that path. And you wind up with:

Easy. And Boring. Sure you can change the width and the color and transparency, but it’s still just a boring line. In order to do a bit more with it, let’s abandon the built-in line functionality and make our own.

The idea is to start at the first point and draw a series of small squares between it and the second point. We’ll use the Pythagorean theorem to get the distance between the two points. That will let us know how many steps we have to take. Then just linearly interpolate between the x and y positions and draw a square there.

const dx = x1 - x0;
const dy = y1 - y0;
const dist = Math.sqrt(dx * dx + dy * dy);

for (let i = 0; i < dist; i++) {
  let t = i / dist;
  let x = x0 + dx * t;
  let y = y0 + dy * t;
  context.fillRect(x - 0.5, y - 0.5, 1, 1);
}

And the result of that:

Not half bad really. Of course, if you want to have a really fast and more accurate line drawing routine, you’d probably want to go with Bresenham’s algorithm or maybe Wu’s algorithm. Honestly, I’m really just referencing them so people don’t comment to correct me and tell me I should be using them. But we’re just getting started here. This still isn’t any more interesting than the built-in line function. It’s just too straight, too even, to regular. Let’s mix it up a bit.

We can start by changing the increment from i++ to, say, i += 5, and get:

Now we have some space between each “pixel” that makes up the line. We can then increase the size of these “pixels”:

context.fillRect(x - 1.5, y - 1.5, 3, 3);

Which has the general effect of pixel art:

In the next example, I changed the increment to 8 and used the following code to draw a circle instead of a square:

context.beginPath();
context.arc(x, y, 4, 0, Math.PI * 2);
context.fill();

And got:

You can keep playing with this, making lines out of any shapes with any amount of spacing. But let’s throw in some randomness.

Instead of drawing a pixel at every single point along the line, let’s draw them at random positions along the line. We can do that just by making t a random value from 0 to 1. Altering the first version, we get:

for (let i = 0; i < dist; i++){
  let t = Math.random();
  let x = x0 + dx * t;
  let y = y0 + dy * t;
  context.fillRect(x - 0.5, y - 0.5, 1, 1);
}

And an image that will look something like this:

Now we’re getting something a bit more interesting.

Let’s play with size and transparency next. I’ll make a size variable that will let us easily change the size of each pixel. And then set the transparency down real low. This will let the pixels overlap and build up more gradually in different areas. I’ll also use dist*2 as the pixel count, which gives us a lot more pixels to play with.

const size = 4;
context.fillStyle = "rgba(0, 0, 0, 0.05)";

for (let i = 0; i < dist*2; i++){
  let t = Math.random();
  let x = x0 + dx * t;
  let y = y0 + dy * t;
  context.fillRect(x - size / 2, y - size / 2, size, size);
}

And that gives us:

Finally, lets go all out, add even more pixels, vary the size, vary the transparency, even vary the position of each individual pixel.

for (let i = 0; i < dist*4; i++){
  let t = Math.random();
  let x = x0 + dx * t;
  let y = y0 + dy * t;
  x += Math.random() * 4 - 2;
  y += Math.random() * 4 - 2;
  let size = Math.random() * 8;
  context.fillStyle = "rgba(0,0,0, " + Math.random()*0.2 + ")";
  context.fillRect(x - size / 2, y - size / 2, size, size);
}

Now we have something that looks kind of like ink on wet paper.

But we don’t have to limit it to straight lines. Here’s the built-in code for drawing a quadratic curve – defined by three points:

const x0 = 100;
const y0 = 700;
const x1 = 400;
const y1 = -100;
const x2 = 700;
const y2 = 700;

context.beginPath();
context.moveTo(x0, y0);
context.quadraticCurveTo(x1, y1, x2, y2);
context.stroke()

This gives you a bland curve:

But we can apply the same concept here. We’ll need a function that gives us a point somewhere along the curve. Fortunately, such a function is well known. We can again choose a random t value and start drawing random pixels. Here’s the code – note I chose an arbitrary value of 3000 for number of pixels. Use more or less to get a denser or lighter line.

for (let i = 0; i < 3000; i++){
  let t = Math.random();
  let p = quadraticPoint(x0, y0, x1, y1, x2, y2, t);
  x = p.x + Math.random() * 4 - 2;
  y = p.y + Math.random() * 4 - 2;
  let size = Math.random() * 8;
  context.fillStyle = "rgba(0,0,0, " + Math.random()*0.2 + ")";
  context.fillRect(x - size / 2, y - size / 2, size, size);
}

function quadraticPoint(x0, y0, x1, y1, x2, y2, t) {
  const oneMinusT = 1.0 - t;
  const m0 = oneMinusT * oneMinusT;
  const m1 = 2.0 * oneMinusT * t;
  const m2 = t * t;
  return {
    x: m0 * x0 + m1 * x1 + m2 * x2,
    y: m0 * y0 + m1 * y1 + m2 * y2,
  }
}

And the curve:

This looks even better drawn with circles:

This looks interesting all by itself, but I suggest you create some parameterized functions for drawing lines, curves and shapes like this and see if it doesn’t change the overall look of your creative pieces.

The whole series of How to Draw a Line:

1. How to Draw a Line

2. More Ways to Draw a Line

3. Yet Another Way to Draw Lines

Probability #2

tutorial

The Monty Hall Problem

You’ve surely heard of this one. It’s a really tough one to wrap your head around. I’ll describe the problem in order to solve it, but it’s worth looking into the background of the “Ask Marilyn” incident if you’re not familiar with it.

Behind Monty Hall’s Doors

The story shows that it’s not just hacks like myself that find this stuff non-intuitive. Lots of highly educated, very smart people got tricked by this one.

The problem is based on an old game show, “Let’s Make a Deal”. The host of the show was named Monty Hall. The contestant is shown three doors. Behind two doors are goats. Behind one is A NEW CAR!!!

The contestant chooses a door. If he chooses the one with THE NEW CAR, he gets to keep the car. If he chooses a goat door, he gets a goat. The unspoken assumption being that he’d rather have a car.

But… before the chosen prize is revealed, Monty Hall opens one of the two remaining doors, revealing a goat!

There are now two doors remaining. The one that the contestant chose, and one other one. Monty asks the contestant if he wants to stay with his choice, or if he’d like to switch to the other door.

What should he do?

To clarify, what should he do in order to optimize his chances of winning THE NEW CAR? Are the odds of winning the car better if he sticks with his original choice? Are they better if he switches? Does it just not matter?

Intuition tells us that it doesn’t matter. There are two doors. One has a goat, one has a car. The odds are 50/50. Doesn’t really matter if he sticks with the first choice or switches. The odds are the same.

The truth though? He should absolutely switch. It will significantly improve his odds of winning. In fact, he’s twice as likely to win if he switches than if he sticks with his original choice.

But why???

If you want a deep explanation… I mean really deep, just head over to the Monty Hall Problem page on wikipedia. But I’ll give my take on it.

When the contestant first chose a door, he had a 1 in 3 chance of winning the car. That’s pretty straightforward.

When Monty opens a door, he is not randomly opening a door. He’s opening a door with a goat behind it, always. So he is using his knowledge of what’s behind each door to change the nature of the game.

Let’s say we have doors A, B, and C. A and B have goats, C has a car.

There are three possible scenarios:

  1. Contestant chooses door A. Monty will open door B. If contestant switches doors, he wins.
  2. Contestant chooses door B. Monty opens A. If contestant switches, he wins.
  3. Contestant chooses door C. Monty opens either A or B. It doesn’t matter. If contestant switches, he loses.

So in two of the three scenarios, switching is the winning move.

Even though I’ve worked out the logic of this myself many times and done it in code more than once, it still feels vaguely magical.

Bring on the Code

Let’s start by playing the game 1000 times in a for loop. And we’ll start by setting up the three doors with goats behind them and swapping out a car on one of them.

// we'll play the game 1000 times.
for (let i = 0; i < 1000; i++) {
  // create 3 doors with goats behind them.
  let doors = ["goat", "goat", "goat"];

  // randomly choose one door and put a car behind it.
  let winner = Math.floor(Math.random() * 3);
  doors[winner] = "car";
  console.log(doors);
}

This outputs the 1000 random door configurations. Something like this:

[ 'car', 'goat', 'goat' ]
[ 'car', 'goat', 'goat' ]
[ 'goat', 'car', 'goat' ]
[ 'car', 'goat', 'goat' ]
[ 'goat', 'car', 'goat' ]
[ 'car', 'goat', 'goat' ]
[ 'goat', 'car', 'goat' ]
[ 'car', 'goat', 'goat' ]
[ 'car', 'goat', 'goat' ]
...

Now let’s have the contestant make a choice – 0, 1 or 2. Just to check ourselves, let’s see whether he wins a car and add up how many times he wins. Out of 1000 games, he should win somewhere close to 333.

let wins = 0;

// we'll play the game 1000 times.
for (let i = 0; i < 1000; i++) {
  // create 3 doors with goats behind them.
  let doors = ["goat", "goat", "goat"];
 
  // randomly choose one door and put a car behind it.
  let winner = Math.floor(Math.random() * 3);
  doors[winner] = "car";
  // console.log(doors);
 
  // choose a door and count how many wins we get
  let choice = Math.floor(Math.random() * 3);
  if (doors[choice] === "car") {
    wins++;
  }
}
// should be around 333
console.log(wins);

That checks out for me.

Next, we let Monty open a door to reveal a goat. There are probably plenty of clever ways to code this, but I’ll just loop through the three doors and choose one that is not the same as what the contestant chose, and make sure that it’s a goat door.

  ...
  // now monty chooses another door (must be a goat!)
  let montyChoice;
  for (let j = 0; j < 3; j++) {
    if (j != choice && doors[j] === "goat") {
      montyChoice = j;
      break;
    }
  }
  console.log(doors[montyChoice]);
  ...

Just to check myself, I logged what’s behind Monty’s door. Sure enough, nothing but goats.

Now we have the contestant’s choice and Monty’s choice. We can add the code back in that calculates how many times the contestant wins the car…

let wins = 0;
// we'll play the game 1000 times.
for (let i = 0; i < 1000; i++) {
  // create 3 doors with goats behind them.
  let doors = ["goat", "goat", "goat"];

  // randomly choose one door and put a car behind it.
  let winner = Math.floor(Math.random() * 3);
  doors[winner] = "car";
  // console.log(doors);

  // choose a door and count how many wins we get
  let choice = Math.floor(Math.random() * 3);

  // now monty chooses another door (must be a goat!)
  let montyChoice;
  for (let j = 0; j < 3; j++) {
    if (j != choice && doors[j] === "goat") {
      montyChoice = j;
      break;
    }
  }
  // console.log(doors[montyChoice]);
 
  // contestant does not switch
  if (doors[choice] === "car") {
    wins++;
  }
}
console.log(wins);

This isn’t any different than the first time we counted the wins. I consistently get numbers in the low 300s. Of course, because the fact that Monty opens a door doesn’t change the fact that there was a 1 in 3 chance of the contestant winning.

But let’s see what happens if the contestant switches doors. Again, you can get fancy here, but I’ll go brute force, looping through the doors till I find the one that is neither the contestant’s choice nor Monty’s choice. And I’ll count how many times he wins with that choice.

let wins = 0;
// we'll play the game 1000 times.
for (let i = 0; i < 1000; i++) {
  // create 3 doors with goats behind them.
  let doors = ["goat", "goat", "goat"];

  // randomly choose one door and put a car behind it.
  let winner = Math.floor(Math.random() * 3);
  doors[winner] = "car";
  // console.log(doors);

  // choose a door and count how many wins we get
  let choice = Math.floor(Math.random() * 3);

  // now monty chooses another door (must be a goat!)
  let montyChoice;
  for (let j = 0; j < 3; j++) {
    if (j != choice && doors[j] === "goat") {
      montyChoice = j;
      break;
    }
  }
  // console.log(doors[montyChoice]);
 
  // contestant does not switch
  // if (doors[choice] === "car") {
  //   wins++;
  // }
 
  // contestant switches
  let newChoice;
  for (let j = 0; j < 3; j++) {
    if (j != choice && j != montyChoice) {
      newChoice = j;
      break;
    }
  }
  if (doors[newChoice] === "car") {
    wins++;
  }
}
console.log(wins);

Running this I get numbers in the upper 600s! Winning roughly 2 out of 3 times, exactly as predicted.

Summary

So there you go. The code proves things out. But it still feels a bit magical.

I did think of another though experiment that helps to make it make sense. Let’s say there were four doors – three goats, one NEW CAR. Contestant chooses one, and Monty Hall opens two of them to reveal two goats. Or better yet, there are ten doors. Contestant chooses one and Monty opens eight. The only way he’d lose by switching is if he had chosen the car to begin with. And there was only a 1 in 10 chance he did that. So if he switches now, he’s got a 9 out of 10 chance of winning! That helps my brain a bit. But still seems a bit magical.

Probability #1

tutorial

I started re-reading The Drunkard’s Walk again today.

I read it a few years ago and remember really liking it. There are lots of examples in there of situations that seem to defy logic, or at least defy our sense of what is logical. But these are provable mathematically using the basic probability.

Whenever I come across a problem like this, even once I get my head around it as much as I can, I like to write some code to prove it out.

One of my favorite such problems is the two children problem. I haven’t gotten to it in the book yet, but I know it’s coming up. It’s a classic. Here it is, my paraphrase:

A woman says she has two children. She says one of them is a boy. What are the odds that the other one is a boy?

The obvious answer is 50%. There’s a child you don’t know about. It’s either a girl or a boy. Everything else is irrelevant, right?

Nope. Actually the odds are 1 in 3 that the other child a boy, 2 in 3 that it’s a girl.

And the really odd part of it is you can change the wording in a way that seems to make no difference, but totally changes it:

A woman says she has two children. She says the first born one is a boy. What are the odds that the other one is a boy?

In this case, yes, the odds are 50% that the other child is a boy.

To understand the odds of a particular situation occurring, such as the genders of two children, you have to consider all possible arrangements and then how many of those arrangements satisfy the criteria. Then divide.

In this case, we have a family who had one child, then another child. They may have had a boy first, then a girl. Or maybe a boy, then another boy. Or a girl and then a boy. Or a girl and another girl. That’s four possibilities:

  • boy boy
  • boy girl
  • girl boy
  • girl girl

So in the first problem, the mother says that one of her children is a boy. This narrows us down to just three possibilities.

  • boy boy
  • boy girl
  • girl boy

In all three of those, one of the children is a boy. In two of them the other child is a girl and in only one, the other child is also a boy. So, 1 in 3 for boy, 2 in 3 for girl.

But let’s look at the second problem. Mom says that her first child is a boy. That gives us only two possibilities:

  • boy boy
  • boy girl

In one of those, the other child is a girl and in one, it’s a boy. 50/50.

Prove it with Code

I’m doing this with JavaScript using node.js. But use whatever you want.

For probability situations, it helps to have a large number of samples. So let’s make 1000 families. Each family will have two children. We can represent these by strings: “bb” means they had a boy, then another boy. “gb” means they had a girl, then a boy, in that order. Likewise, “bg” means the opposite order and “gg” means they had two girls. We’ll store all the families in an array, and we’ll actually go through one by one, randomly choosing the gender of the first child, and then the second child.

let families = [];

// create 1000 families with two randomly gendered children.
for (let i = 0; i < 1000; i++) {
  // no kids yet.
  let family = "";

  // first child
  if (Math.random() < 0.5) {
    family += "b";
  } else {
    family += "g";
  }

  // second child
  if (Math.random() < 0.5) {
    family += "b";
  } else {
    family += "g";
  }

  families.push(family);
}
console.log(families);

This should give you something like the following output, with 1000 total strings.

['gb', 'gb', 'gg', 'gb', 'gb', 'gg', 'bb', 'bg', 'bb', 'gb',
'bg', 'gg', 'gg', 'bg', 'bg', 'gg', 'gg', 'bg', 'gb', 'gg',
'gg', 'gb', 'bb', 'bg', 'gg', 'gb', 'gg', 'gb', 'bb', 'gb',
'bg', 'bb', 'gg', 'gb', 'gb', 'bb', 'bg', 'bg', 'gb', 'bb',
'gb', 'gb', 'gg', 'gg', 'bb', 'bb', 'bb', 'gb', 'gg', 'gb',
'gb', 'bg', 'gg', 'bg', 'bg', 'gb', 'bg', 'gg', 'gg', 'bg',
'bb', 'gb', 'bg', 'gb', 'gg', 'gg', 'bg', 'bg', 'gg', 'bb',
'gb', 'bg', 'gg', 'gb', 'bg', 'bg', 'bg', 'gg', 'bb', 'gb',
'gg', 'bb', 'bb', 'gg', 'gg', 'bb', 'gg', 'gg', 'bg', 'bb',
'bb', 'gg', 'gg', 'gg', 'gg', 'bg', 'gg', 'gg', 'bg', 'gg',
...
]

We have multiples of every type of family in there: “bb”, “bg”, “gg”, “gb”. Now, the mother said that one of the children was a boy. So let’s filter this down to only the families that have a “b” in them.

// now lets get all the families who have at least one boy
let oneBoyFamilies = families.filter(family => family.indexOf("b") > -1);
console.log(oneBoyFamilies);

My var name isn’t the greatest. Consider oneBoyFamilies to mean “at least one boy”. This should give you something like the following output.

['gb', 'gb', 'gb', 'gb', 'bb', 'bg', 'bb', 'gb', 'bg', 'bg',
'bg', 'bg', 'gb', 'gb', 'bb', 'bg', 'gb', 'gb', 'bb', 'gb',
'bg', 'bb', 'gb', 'gb', 'bb', 'bg', 'bg', 'gb', 'bb', 'gb',
'gb', 'bb', 'bb', 'bb', 'gb', 'gb', 'gb', 'bg', 'bg', 'bg',
'gb', 'bg', 'bg', 'bb', 'gb', 'bg', 'gb', 'bg', 'bg', 'bb',
'gb', 'bg', 'gb', 'bg', 'bg', 'bg', 'bb', 'gb', 'bb', 'bb',
'bb', 'bg', 'bb', 'bb', 'bg', 'bg', 'bg', 'bb', 'bb', 'bb',
'gb', 'bb', 'bb', 'bb', 'bg', 'bg', 'bg', 'gb', 'bb', 'bb',
'gb', 'bb', 'bg', 'bg', 'bb',
...
]

I don’t see any “gg”s in there, but just to be sure, we can say:

// validate that there are no families with two girls here
console.log(oneBoyFamilies.indexOf("gg"));

If we’ve done that right, we should get a -1 here. I do, so I’m satisfied.

Now, to find families where the other child is a boy, we need to look for families consisting of “bb”. But for families where the other child is a girl, we need to look for either “gb” or “bg”. Already you can see why it’s 2 to 1 in favor of the other child being a girl. But here’s the code:

let otherChildIsABoy = oneBoyFamilies.filter(family => family === "bb");
let otherChildIsAGirl = oneBoyFamilies.filter(family => family === "bg" || family == "gb");
console.log("boy: " + otherChildIsABoy.length);
console.log("girl: " + otherChildIsAGirl.length);

Alternately, for the girls, you could do something like:

let otherChildIsAGirl = oneBoyFamilies.filter(family => family.indexOf("g") > -1);

Shouldn’t make any difference. With either method, I consistently get in the mid-200s for boys and around 500 for girls. Total would be around 750-ish, which makes sense since we filtered out one of the four arrangements (“gg”). So, 1 in 3 for boys, 2 in 3 for girls. Spot on.

Finally, let’s do the second version. Here, the mom says her first child is a boy. So for that we have to search for families where the first character is a “b”. Then we just get the count of the resulting families where the second character is a “b” and the count where it’s “g”.

// this time, lets get all the families where the FIRST child is a boy
let firstBoyFamilies = families.filter(family => family.charAt(0) === "b");
console.log(firstBoyFamilies);

let secondChildIsABoy = firstBoyFamilies.filter(family => family.charAt(1) === "b");
let secondChildIsAGirl = firstBoyFamilies.filter(family => family.charAt(1) === "g");

console.log("boy: " + secondChildIsABoy.length);
console.log("girl: " + secondChildIsAGirl.length);

The results you see here will vary. Sometimes you’ll get more boys than girls as the second child. Sometimes the opposite. Occasionally they’ll be dead even. We’ll call that 50%.

Why am I doing this?

This is firmly in the realm of recreational mathematics. I just find it really interesting to prove these things out even when my brain doesn’t agree 100% all the time. I enjoy doing it and I’ll probably do some more.

PS:

Here’s all the code in one spot:

let families = [];
// create 1000 families with two randomly gendered children.
for (let i = 0; i < 1000; i++) {
  // no kids yet.
  let family = "";

  // first child
  if (Math.random() < 0.5) {
    family += "b";
  } else {
    family += "g";
  }

  // second child
  if (Math.random() < 0.5) {
    family += "b";
  } else {
    family += "g";
  }

  families.push(family);
}
console.log(families);

// now lets get all the families who have at least one boy
let oneBoyFamilies = families.filter(family => family.indexOf("b") > -1);
console.log(oneBoyFamilies);

// validate that there are no families with two girls here
console.log(oneBoyFamilies.indexOf("gg"));

let otherChildIsABoy = oneBoyFamilies.filter(family => family === "bb");
let otherChildIsAGirl = oneBoyFamilies.filter(family => family === "bg" || family == "gb");
// alternately…
// let otherChildIsAGirl = oneBoyFamilies.filter(family => family.indexOf("g") > -1);

console.log("boy: " + otherChildIsABoy.length);
console.log("girl: " + otherChildIsAGirl.length);

// this time, lets get all the families where the FIRST child is a boy
let firstBoyFamilies = families.filter(family => family.charAt(0) === "b");
console.log(firstBoyFamilies);

let secondChildIsABoy = firstBoyFamilies.filter(family => family.charAt(1) === "b");
let secondChildIsAGirl = firstBoyFamilies.filter(family => family.charAt(1) === "g");

console.log("boy: " + secondChildIsABoy.length);
console.log("girl: " + secondChildIsAGirl.length);

Grep and Sed, Demystified

tutorial

I’ve kind of half understood grep for a while, but assumed that I didn’t really get it at all. I thought I knew nothing at all about sed. I took some time this weekend to sit down and actually learn about these two commands and discovered I already knew a good deal about both of them and filled in some of what I didn’t know pretty easily. Both are a lot more simple and straightforward than I thought they were.

Grep

grep comes from “global regular expression print”. This is not really an acronym, but comes from the old time ed line editor tool. In that tool, if you wanted to globally search a file you were editing and print the lines that matched, you’d type g/re/p (where re is the regular expression you are using to search. The functionality got pulled out of ed and made into a standalone tool, grep.

Basics

grep, in its simplest use, searches all the lines of a given file or files and prints all the lines that match a regular expression. Syntax:

grep <options> <expression> <file(s)>

So if you want to search the file animals.txt for all the lines that contain the word dog, you just type:

grep dog animals.txt

It’s usually suggested that you include the expression in single quotes. This prevents a lot of potential problems, such as misinterpretations of spaces or unintentional expansion:

grep 'flying squirrel' animals.txt

It’s also a good idea to explicitly use the -e flag before the expression. This explicitly tells grep that the thing coming next is the expression. Say you had a file that was list of items each preceded with a dash, and you wanted to search for -dog

grep '-dog' animals.txt

Even with the quotes, grep will try to parse -dog as a command line flag. This handles it:

grep -e '-dog' animals.txt

You can search multiple files at the same time with wildcards:

grep -e 'dog' *

This will find all the lines that contain dog in any file in the current directory.

You can also recurse directories using the -r (or -R) flag:

grep -r -e 'dog' *

You can combine flags, but make sure that e is the last one before the expression:

grep -re 'dog' *

A very common use of grep is to pipe the output of one command into grep.

cat animals.txt | grep -e 'dog'

This simple example is exactly the same as just using grep with the file name, so is an unnecessary use of cat, but if you have some other command that generates a bunch of text, this is very useful.

grep simply outputs its results to stdout – the terminal. You could pipe that into another command or save it to a new file…

grep -e 'dog' animals.txt > dogs.txt

Extended

When you get into more complex regular expressions, you’ll need to start escaping the special characters you use to construct them, like parentheses and brackets:

grep -e '\(flying \)\?squirrel' animals.txt

This can quickly become a pain. Time for extended regular expressions, using the -E flag:

grep -Ee '(flying )?squirrel' animals.txt

Much easier. Note that -E had nothing at all to do with -e. That confused me earlier. You should use both in this case. You may have heard of the tool egrep. This is simply grep -E. In some systems egrep is literally a shell script that calls grep -E. In others it’s a separate executable, but it’s just grep -E under the hood.

egrep -e '(flying )?squirrel' animals.txt

Other Stuff

The above covers most of what you need to know to use basic grep. There are some other useful flags you can check into as well:

-o prints only the text that matches the expression, instead of the whole line

-h suppresses the file name from printing

-n prints the line number of each printed line.

Simple Text Search

If the thing you are searching for is simple text, you can use grep -F or fgrep. All the same, but you can’t use regular expressions, just search for a simple string.

grep -Fe 'dog' animals.text
fgrep -e 'dog' animals.text

Perl

There’s also grep with Perl syntax for regular expressions. This is a lot more powerful than normal grep regex syntax, but a lot more complex, so only use it if you really need it. It’s also not supported on every system. To use it, use the -P flag.

grep -Pe 'dog' animals.text

In this simple case, using Perl syntax gives us nothing beyond the usual syntax. Also note that pgrep is NOT an alternate form of grep -P. So much for consistency.

Sed

I thought that I knew next to nothing about sed, but it turns out that I’ve been using it for a few years for text replacement in vim! sed stands for “stream editor” and also hails from the ed line editor program. The syntax is:

sed <options> command <file(s)>

The two main options you’ll use most of the time are -e and -E which work the same way they do in grep.

The most common use of sed is to replace text in files. There are other uses which can edit the files in other ways, but I’ll stick to the basic replacement use case.

Like grep, sed reads each line of text in a file or files and looks for a match. It then performs a replacement on the matched text and prints out the resulting lines. The expression to use for replacement is

s/x/y/

where x is the text you are looking for, and y is what to replace it with. So to replace all instances of cat with the word feline in animals.txt

sed -e 's/cat/feline/' animals.txt

Note that sed will print every line of text in the file, whether or not it found a match or not. But the lines that it matched will be changed the way you specified.

After the final slash in the expression, you can add other regex flags like g for global or i for case insensitivity.

Like grep, sed just outputs to stdout. You can redirect that to another file using > or pipe it to another process using |. But do NOT save the output back to the original file. Try it out some time on a test file that you don’t care about and see what happens.

There are lots of other options you can use with sed but the above will probably get you by for a while to come. As you need more, just read up.

Summary

The biggest thing I took away from the couple of hours I spent on this was actually how easy these two commands were to learn. I’d been avoiding doing so for a long time and now wish that I had spend the effort on this much earlier.