2023 Retrospective

misc

Another year, another look back at another year.

Last year about this time I made some goals. Let’s see how I did on those…

  1. Create my own interpreted language. Nope. Didn’t do anything with that this past year. I’ve done some work around this in the past, but never made anything final. I’d still like to do this someday, but it’s gonna be on hold for a while. Don’t expect I’ll do much with this in 2024.
  2. I want to finally do something with music. I did!!! I got into Supercollider and did a pretty deep dive. I even published some things on SoundCloud. https://soundcloud.com/bit101. This is by far the most I’ve ever done with music and audio. I wound up taking a break from it towards the end of the year, but I do plan on getting back into it. More on that later.
  3. I’d love to do another side project creating graphics for something. Yes, I did this too. I finished a new design for a wine bottle label for Anarchist Wine recently. It’s not out yet, but most of what you see on that page are designs I did in the past. Sometimes they just find something I posted somewhere and ask if they can use it (and pay for it). Other times it’s a more collaborative effort where I come up with a concept and we iterate on it. The fun ones are data driven. We gather some numbers on a given subject or theme and work them into the design. It’s somewhat like data visualization, but usually way more abstracted. Beyond the one I finished recently, I’m at work on another one as I write this. And there are more planned in the coming year. So that’s fun! But I’d still love to do more projects like this for other companies or brands. So if you need work like this, or know someone who does, hit me up.
  1. Of course, I’ll finish the Coding Curves project. And I did finish. That was really fun and I like how it all worked out. I mentioned that I’d like to maybe publish it as a book at some point. I’d still like to. I had another project in mind, Coding Color, and I put an initial post up about that. But didn’t get too far. I got a bit bogged down in how to do it in a cross-platform/language/api way, since every library seems to treat colors a bit differently. But it’s definitely something I’d like to tackle more at some point. I guess that’s one goal for 2024.

Music

I want to talk a bit more about music, as this was the biggest, most interesting thing I got into in 2023. I learned so much – about music, but also about myself and what I want to be doing. For ages I felt bad that I knew nothing about music and had no abilities in the area. This exploration got me over that feeling. I have created some music now and I have enough tools and knowledge to do more if I want to. But as I got more into it, I started questioning what exactly my goal was with it. I realized that I have no goal at all of becoming an electronic (or any other kind of) musician. I don’t want to create traditional “songs” per se, or “drop tracks and albums”. I don’t want to sell music or in any way support myself through that.

In short, I don’t really want to do music for music’s sake. I loved the hell out of learning what I learned and want to learn more. My initial goal was to be able to make music, or audio soundscapes for animations and videos that I code. I feel like I am kind of at square one of being able to do that now.

So another goal for 2024 is to actually make some longer form (2-3 minutes or longer) generative videos with my own soundtracks.

Another thing I learned in my foray into music was how much I love coding graphics and animations, how good I am at it (yeah, a little brag there), and how comfortable I am sharing that stuff. Coding music is still a bit of a struggle for me. It doesn’t come easily and I’m still really hesitant to share what I make. But I’m getting better at it.

Part of the hesitancy in sharing is that I feel like asking someone to listen to a song is a much bigger ask than asking them to look at an image or a short animation. You post an image and it’s there and they see it and that’s that. Listening to a song is a small investment of time and attention.

One thing I know will kick me into gear musically is this book I pre-ordered that will arrive in January: Supercollider for the Creative Musician by Eli Fieldsteel. HIs Youtube channel is amazing and one of the resources I hit up over and over and over when I was learning about Supercollider (and music/audio concepts in general).

ABC – Always Be Creating

In general, I just want to keep on creating cool and interesting stuff and sharing it. Whether that’s images, animations, tutorials, or music, it’s all good. I’ll be looking at more community driven projects, like Genuary to participate in. I’m going to shoot for 100% participation in Genuary. I’ll be posting on Mastodon at https://mstdn.social/@bit101.

I’ll be looking for more stuff like this throughout the year, either actual projects like https://7daysofcode.art/ or just hashtag-based projects. Seems like there are more and more things like this popping up, which is great.

30 Days of Supercollider Series

misc

This will be an index of the articles I post about Supercollider.

Warning: I don’t know a lot about Supercollider yet. This will be a journal of my discoveries as much as anything else. I probably know less about music in general. Trying to learn something before my last trip around the sun. Anyway, this shouldn’t be taken as a step-by-step tutorial on learning Supercollider. Just a random collection of stuff.

The Days:

  1. The IDE
  2. Functions
  3. More Function Stuff
  4. Variables, etc.
  5. Unit Generators
  6. Envelopes

In the off chance you might be interested in the actual sounds I’m creating, you can find them here:

Let there be sound

misc

And there was sound.

Plans

As 2023 is now half over (wtf?), it’s time to look back on my end of year post, where I made some plans. https://www.bit-101.com/blog/2022/12/

  1. Create my own interpreted language.
  2. I want to finally do something with music.
  3. I’d love to do another side project creating graphics for something.
  4. Of course, I’ll finish the Coding Curves project.

Not sure if number 1 will happen this year. Or number 2. I did wrap up Coding Curves! So that’s good.

Music?

But what about that music thing? My goal was to learn enough about music to actually release a song. Releasing meaning posting on this blog. Song meaning a sequence of sounds that is vaguely musical.

As mentioned in the post, I was dabbling with MilkyTracker at the time. It was fun, but wasn’t quite getting me where I wanted. Then I took a deep dive into LMMS. Also very powerful and fun, but was still a bit frustrating to me. Both these tools felt like they were leading me down a path to create a very specific type of music with predefined conventions. And fiddling with hundreds of settings in scores of UIs just wasn’t cutting it for me.

Supercollider!

So I took a break for a while. Then recently I started looking into Supercollider again. I’d dipped my toes into it once or twice in the past. I found a few different resources, but then ran across this series. https://www.youtube.com/playlist?list=PLPYzvS8A_rTYEba_4SDvRJyIyjKaDNjn9 This is actually a college course on Supercollider and it is really good. The teacher is also writing a book on SC which will be out later this year.

I also just took delivery of The Supercollider Book by MIT Press.

This book was published in 2011, but Supercollider is pretty stable, and there are online resources, so if there are any discrepancies, I can hopefully figure them out quickly. This isn’t a cover-to-cover tutorial type book anyway, but more a series of long articles on different subjects. Good for a deep dive on a particular aspect of SC.

Where I’m at

So, here is a … “song” that I created.

I put “song” in quotes because it’s really just a stochastic series of notes. But I think it sounds rather pleasant, even if it’s way too unstructured. It’s a single synth definition, creating three different synths with different settings, playing random notes from a pentatonic scale with random but quantized timings. This isn’t straight up copied from a tutorial. I got some basics, researched some other stuff, played around and came up with some ideas of my own. Very fun.

I’m not counting this as my goal of releasing a song though. This is the equivalent of drawing 1000 random lines and calling it art. OK, maybe. But maybe not. But maybe.

But I’m currently very excited about this. Supercollider is just straight up writing code in a code editor, executing that code, and having music come out. That works with how my brain has been used to working with graphics over the last 25 years. I’m not great at tools like Illustrator or Photoshop, etc. But give me a graphics api and I can code cool images for days.

Actually, my forays into Supercollider remind me a whole lot of my early days in Flash and ActionScript. I had no idea what I was doing at first, but I was able to draw a circle. Then I was able to make that circle move with code. Then I got it to bounce off the edges of the screen. I added gravity, and dampening, and bouncing coefficients. Then I got drag and drop working and even made it so you could throw the ball. I wrote all that up in my Gravity Tutorial back in the late 90s. It predates “bit-101” by a couple of years. That article right there was the start of everything I’ve done since in technology.

With sound and music, I feel like I’m just about at that point where I have the circle bouncing off the sides of the screen. There’s so much more to learn and I feel like I can just dive into different topics and add little bits of knowledge to what I already know and make cooler and more interesting soundscapes.

The 2000’s were great, discovering all the different things I could do with Flash. Graphics, animation, physics, math, fractals, art, etc. In the 2010’s, HTML and JavaScript started taking over. You could now do most of what you could do in Flash right on the web, with no plugins, either with 2D canvas, or WebGL. Even SVG got pretty powerful. I got on the canvas bandwagon and did that for several years. But a whole lot of what I was doing was rehashing ideas I came up with in Flash the previous decade. About 6 years ago I started coding in Go with Cairographics and I’ve really been enjoying that. But again, doing a lot of the same themes over again. Yeah, I’ve found plenty of new things and have leveled up a lot of different techniques, but it’s not the same as that first bunch of years of pure discovery.

I’ve felt like I’m constantly looking around for some new graphical technique that will excite me for years. Now I’m thinking that switching from graphics to audio might be exactly the kind of move I’ve been looking for. There’s SO MUCH to learn. There’s so much stuff that I just don’t have a clue about. It’s daunting, but very exhilarating. Because I feel I CAN learn it. And it’s all brand new.

There’s also the allure of creating the graphics AND accompanying music for an animated piece. I’ve done a collaboration or two with a friend who actually knows how to make music, and I have another collab I’m working on with another friend, and I’ve created some music with Garageband to go with one animation. That worked out better than it might sound, but to write the visuals and the audio myself, in code. That’s the holy grail. So, we’ll see where this goes…

Coding Curves 12: Guilloche Patterns

coding curves, misc

Chapter 12 of Coding Curves

Guilloche patterns are very intricate and fascinating patterns. You’ll often find them on bank notes and other official documents as well as watches and other intricate machinery. Because they are so intricate and complex, and often engraved into metal, they are usually done by machines themselves. Imagine a high end spirograph machine with a metal etching tool rather than a ball point pen. The name “Guilloche pattern” is rather vague and can apply to all kinds of similar patterns. I’m going to explain how to make a pattern like the one you see here:

three ringed guilloche pattern

This is a lot like the kind of thing you’d see on some kind of certificate or bank note, and once you understand what’s going on, you can adapt the code to make other similar patterns.

Step One – A Simple Ring

We’ll start by making a single, simple ring that looks like this:

a circle composed of many looping waves

This is very much like a trochoid or a rose curve. In fact, you could probably make this with those formulas, but I’m going to do it a bit differently to set it up for the more complex stuff we’ll do later on.

This is essentially a sine wave that’s wrapped around a circle. Note that it has an inner radius and an outer radius. The sine wave has 80 nodes, but it overlaps itself. To simplify what’s happening here, I’ll increase the inner radius and make it so it doesn’t overlap:

a circle drawn with a sine wave instead of a single curve

Now you can see more clearly that this is, as I said, a sine wave wrapped around a circle. Now I’ll add a little bit of overlap:

a circle drawn with an overlapping sine wave

Here, you should still be able to see the sine wave even with the overlap. The first image is the same idea, but with a lower inner radius and more overlap. Now let’s look how to draw this.

We start with an inner and outer radius. With a bit of easy math we can figure out a “mid” radius. This will be the the zero-point of the sine wave. And we’ll need a range, which is how much the sine wave will have to extend to either side of this radius to hit the inner and outer limits. You might also call this the amplitude of the wave.

width = 600
height = 600
canvas(width, height)

translate(width / 2, height / 2)

inner = 50
outer = 250
range = (outer - inner) * 0.5
mid = inner + range

Next, we need values for how many cycles in the wave, and how much it overlaps. I’ll call these nodes and div. These should be whole numbers and should not be evenly divisible. This is very similar to the n and d parameters in the rose curves we made in the last chapter, but I’ll name them differently to keep things clear.

nodes = 80
div = 11

Now we can loop t from 0 to 2 * PI * div and draw some line segments. The angle of each new point for the next segment is simply t and the radius will be computed as shown. We need to multiply 2 * PI by div to make sure we go around the circle enough times to meet back up correctly.

for (t = 0; t < 2 * PI * div; t += 0.01) {
  radius = mid + sin(t * nodes / div) * range
  x = cos(t) * radius
  y = sin(t) * radius
  lineTo(x, y)
}
stroke()

If you look back at the roses chapter, you’ll see this is very similar to how we got the radius there, but rather than figuring the radius as a sine and single multiplication, we’re using mid and range to fit the radius between inner and outer.

You can play with this a bit. Try different inner and outer radii and number of nodes and values for div. For a good Guilloche pattern, you probably want to keep nodes rather high, and div lower. But Most importantly, they should not be evenly divisible. An easy formula for decent patterns is to make div a small prime number and make sure nodes is not a multiple of div.

For example, if div is 17, you wouldn’t want to use 170 for nodes, or you’ll get this:

a rounded star kind of shape with 10 nodes

But changing nodes to 171 gives you a much better pattern:

a circle drawn with many overlapping loops

If you want to play with an interactive version of this, I made this a few years back:

https://bit101.github.io/lab/dailies/170120.html

Step 2 – A Complex Ring

Next, we’ll add some complexity to the mix. Rather than having our sine wave go back and forth between fixed inner and outer radii, we’ll have these radii themselves vary with a separate sine wave each! The result will look like this:

a single ring of a guilloche pattern

We’ll need a few more parameters for this. To calculate the final outer radius at any point, we’ll need the base outer radius, how many cycles of that sine wave and how far the final radius will differ from the base outer radius. And we’ll need the same three values for the inner radius. And of course we’ll still need nodes and div. The values I used for the above image are:

inner = 100.0
n0 = 7.0
h0 = 10.0

outer = 250.0
n1 = 17.0
h1 = 20.0

nodes = 142.0
div = 89.0

So the inner radius will have 7 nodes, and the radius will vary from 90 to 110, which means a base of 100, plus or minus 10. Similarly the outer radius will have 17 nodes and vary between 230 and 270.

All the code we did early to find the “mid radius” and range for the sine wave will have to be redone for each iteration of the for loop now, using these values and the dynamic inner and outer radii.

for (t = 0; t < 2 * PI * div; t += 0.01) {
  r0 = inner + sin(t * n0) * h0
  r1 = outer + sin(t * n1) * h1

  range = (r1 - r0) * 0.5
  mid = r0 + range

  radius = mid + sin(t * nodes / div) * range
  x = cos(t) * radius
  y = sin(t) * radius
  lineTo(x, y)
}
stroke()

You can surely simplify this code, but I wanted to write it all out explicitly for the sake of clarity.

Again this is a good stopping point to play with some of the parameters and see the different types of shapes you can make. We have one more level of complexity to tackle next.

Here’s an interactive version of this step:

https://bit101.github.io/lab/dailies/170121.html

Step 3 – Multiple Rings

The next step is to create multiple rings that will exactly fit together, like the image that appears at the start of this chapter:

a three ringed guilloche pattern

This is easier than it might seem. If you draw one smaller ring, and then one larger one around that, you just have to make sure that the parameters for the larger ring’s inner radius match the parameters for the smaller ring’s outer radius. Since we’ll be calling the same code multiple times, this would be a good time to turn it into a reusable function. For the most part, this just means wrapping the for loop and stroke call in a function with all the parameters passed in. I also added x and y parameters, so you can center the pattern anywhere on the canvas.

function guilloche(x, y, ir, n0, h0, or, n1, h1, nodes, div) {
  for (t = 0; t < 2 * PI * div; t += 0.01) {
    r0 = ir + sin(t * n0) * h0
    r1 = or + sin(t * n1) * h1

    range = (r1 - r0) * 0.5
    mid = r0 + range

    radius = mid + sin(t * nodes / div) * range
    lineTo(x +cos(t) * radius, y +sin(t) * radius)
  }
  stroke()
}

I upped the canvas size to 800×800 and then used this set of function calls:

guillloche(400, 400, 50, 6, 10, 120, 12, 20, 137, 37)
guillloche(400, 400, 120, 12, 20, 220, 18, 30, 141, 41)
guillloche(400, 400, 220, 18, 30, 350, 24, 20, 164, 53)

This resulted in the following image:

another three ringed guilloche pattern

Note that in the first call, the outer radius params are 120, 12 and 20. These correspond to the inner radius params in the next call. And the outer radius params for the second call, 220, 18 and 30, correspond to the inner radius params on the final call. This way, each ring lines up perfectly.

As usual, play around with this. You can make as many ring as you want. You might want to set up a custom data type that encapsulates all of the radius parameters to make that part more reusable. I’ll leave it to you.

Another thing you might want to try is making each ring a different color:

a colored three ringed guilloche pattern

Here’s an interactive version of the final product:

https://bit101.github.io/lab/dailies/170122.html

Note that there’s no rule that says the rings have to match exactly. Try putting some space between them or letting the overlap and see what kind of interesting patterns you can create that way. This next example has the first and last rings the same as in the previous example, but the middle ring is defined quite differently:

guillloche(400, 400, 20, 4, 5, 120, 8, 10, 137, 37)
guillloche(400, 400, 160, 5, 24, 160, 11, 24, 80, 17)
guillloche(400, 400, 220, 18, 16, 350, 10, 20, 164, 53)

Here, the inner and outer radius is the same, 160, which makes some interesting blobs rather than a discrete ring. All is fair in Guilloche patterns!

a three ringed guilloche pattern. the inner ring does not tightly interlock with the others

And of course, don’t forget to animate!

an animated guilloche pattern

Summary

Do an image search for Guilloche patterns and you’ll find things that are much different, and often more complex than what I’ve presented here. This is just one take on them.

You might try altering the code do draw something other than circles. Ellipses would be a good place to start. And then move onto other shapes. But all that is a bit beyond the scope of this chapter.

2022 in Review

misc

I’ll keep this light this year. Professionally, a strange and difficult year. But at least I still have a job, so I’m grateful for that.

Beyond the job though, I did do some fun coding projects.

Probably the biggest was my raytracing journey. I’ve never been super interested in that kind of photo-realistic rendering from an artistic viewpoint. But working through the books and creating the renderer itself was the most fun I’ve had coding in ages. Of course, once it was sufficiently “complete”, I kind of lost interest in it. It didn’t really change the basic fact that 3D stuff is not what I’m really interested in creatively. That said, I’ll probably play with it some more in the future. My original thinking was to do some kind of generative stuff that could then be rendered in 3D. But “generative” fairly often means lots of individually created units interacting in some way – at least the way I usually do generative. so it can get expensive rendering hundreds or thousands of objects. I might need to look more into optimizing the engine – I skipped that chapter in the book!

Other than that, I created a few vim plugins. One of which, bufkill, I use on a regular basis and really love. I made a few others, but none that were really worth talking about too much. During my plugin-writing phase, I also did a lot of upgrading of my neovim setup, switching my whole config to use Lua, and dropping Conquer of Completion in favor of neovim’s built-in LSP system for completion and syntax checking. That was a lot of work, but I’m happy with the result.

And of course, I started the Coding Curves series, which I’m still really excited about. As I said in the initial post, this was a book that I’ve wanted to write for years. I finally came to the conclusion that I was not going to sit down and write it from start to finish, but writing each chapter as a blog post was way more doable. I banged out ten “chapters” so far and they’ve been pretty well received. I’m taking a short break, but will be back to do the rest before too long. Most of what’s left I already have lots of code and examples written for, and a lot of the explanations are kind of half written in my head already.

I haven’t put out a lot of creative-code artwork this year, but in the last few weeks I’ve been working hard to upgrade my personal code libraries. I currently use a couple of libraries of my own in every creative coding project I do: blcairo which is the drawing api with a ton of custom rendering routines, and bitlib which has math, geometry, color, a PRNG, noise, and other various utilities I use all the time. I love this library and I’ve added a lot to it in recent months.

Oh, and I quit Twitter. I fully deleted my account and set up a new private account with the old user name just to keep anyone from taking it over and impersonating me. It was tough to walk away from 8,000 followers, but every time I see the toxic mess that the platform has become, I have zero regrets. Find me now at https://mstdn.social/@bit101. I remain very happy with Mastodon.

Plans for 2023

There are a few things I want to do in the new year.

  • Create my own interpreted language. A while back I got the book Writing an Interpreter in Go and worked partway through it. It’s quite good and I want to do more with this. My idea would be a simple interpreted language that would depend on my own Go libraries as described above. This would probably just be for my own education and personal use. I don’t have a lot of interest in supporting a language in the wild.
  • I want to finally do something with music. At this stage in life, I doubt I will ever learn any kind of instrument, but I have enjoyed doing stuff with electronic music on and off over the years. I’m currently messing around with MilkyTracker, which reminds me of playing with mod trackers back in my Amiga years. I know trackers are kind of toys, but that’s all I’m really looking for now. Something to play with and have fun and maybe learn a bit. Eventually I might like to dip my toes into musical hardware, synths, sequencers, etc. Anyway, one of my goals will be to release a song in 2023. “Release a song” sounds way more serious than I mean it to sound. I will create a song and put it on my web site and hopefully it won’t suck too much (but, fair warning, it probably will). If anyone has any advice or resources for learning this stuff, let me know.
  • I’d love to do another side project creating graphics for something. My last big projects were back in 2018. I’m open for projects!
  • Of course, I’ll finish the Coding Curves project. Probably in January or February. I’m toying with the idea of a similarly structured project with a different subject, tba.

Coding Curves 08: Bézier Curves

misc

Chapter 8 of the Coding Curves Series

I had to hold myself back here. Bézier curves are fun to program, fascinating to explore, and you can go down a deep hole in explaining how they are constructed and what the formulas mean. The thing is, I’ve already done that a few times. In books and in videos. Here’s a couple of my own videos you might want to check out to learn more:

https://www.youtube.com/watch?v=dXECQRlmIaE: Coding Curves 08: Bézier Curves https://www.youtube.com/watch?v=2hL1LGMVnVM: Coding Curves 08: Bézier Curves

And here are a couple of amazing videos by Freya Holmer:

https://www.youtube.com/watch?v=aVwxzDHniEw: Coding Curves 08: Bézier Curves https://www.youtube.com/watch?v=jvPPXbo87ds: Coding Curves 08: Bézier Curves

So I’m going to limit myself here to the bare basics, some functions, and some cool, practical tips and tricks I’ve discovered over the years.

The Basics

A Bézier curve is defined by two end points and one or more control points. It starts at one end point, curves towards (but not through) the control point(s) and ends at the other end point. By moving any of the points, you alter the shape of the curve. These curves are generally visually pleasing and are used in all kinds of design tools, and are a key part of the shapes of things from fonts to cars.

There are two types of Bézier curves that you’ll run into most often.

Quadratic Bézier Curves

These are defined by two end points and a single control point. Here’s an example:

The control point is the one near the bottom of the canvas. If I move that to the right, it changes the curve:

The lighter lines and black points I just threw in for visual context.

Cubic Bézier Curves

Cubic Bézier Curves have two end points and two control points. Example:

It is possible to have higher order Bézier curves with more control points, but the math gets more and more costly. See Freya’s videos above for some explanations about this.

Most drawing apis have methods for both quadratic and cubic curves, but how they are names varies greatly.

I’ve seen quadratic Bézier curve methods named:

  • curveTo
  • quadraticCurveTo

And cubic Bézier curve methods named:

  • curveTo
  • cubicCurveTo
  • bezierCurveTo

So make sure you know what the method names are for your api. A common strategy, as seen in the examples above, is to have the starting point defined by using a moveTo, or the last known position of the drawing cursor, and then have the curve method just define the control points and the final end point. So you’d do something like:

moveTo(100, 100)
cubicCurveTo(200, 100, 200, 500, 100, 300)
stroke()

But some apis may have other methods that allow you to specify all the points at once.

That’s about it for the basics and built-ins, but of course, we’ll now leave the apis behind and code up some curves ourselves.

Coding Bézier Curves

We’ll start with quadratic curves and then move on to cubic. But before we create the methods that draw the paths, we’re going to create another, more basic method. This will give us the point at any interval along the Bézier curve.

Quadratic

Interestingly, the basic formulas for Bézier curves are one-dimensional. To make two-dimensional, three-dimensional or higher Bézier curves, you just apply the formula once for each dimension. We’ll be sticking two 2D here, so we’ll be doing this twice. The single parametric formula is:

x = (1 - t) * (1 - t) * x0 + 2 * (1 - t) * t * x1 + t * t * x2

Here, x0, x1, and x2 are the end and control “points” and t is a value that ranges from 0.0 to 1.0. This returns the x value along the Bézier path corresponding to the value of t. When t is 0, x is equal to x0. When t is 1, x is equal to x2. When t is between 0 and 1, x will be interpolated.

So to make a 2D quadratic Bézier point function we do this:

function quadBezierPoint(x0, y0, x1, y1, x2, y2) {
  x = (1 - t) * (1 - t) * x0 + 2 * (1 - t) * t * x1 + t * t * x2
  y = (1 - t) * (1 - t) * y0 + 2 * (1 - t) * t * y1 + t * t * y2
  return x, y
}

You can do this if your language lets you return multiple values. Otherwise, you’ll have to encode x, y in come kind of point object.

Note that we have a lot of duplication going on there. We can clean it up first factoring out all those 1-ts:

function quadBezierPoint(x0, y0, x1, y1, x2, y2, t) {
  m = (1 - t)
  x = m * m * x0 + 2 * m * t * x1 + t * t * x2
  y = m * m * y0 + 2 * m * t * y1 + t * t * y2
  return x, y
}

And then:

function quadBezierPoint(x0, y0, x1, y1, x2, y2, t) {
  m = (1 - t)
  a = m * m
  b = 2 * m * t
  c = t * t
  x = a * x0 + b * x1 + c * x2
  y = a * y0 + b * y1 + c * y2
  return x, y
}

If nothing else, this makes it much easier to read.

Now that we have this, we can make a function to draw quadratic Bézier curves. To make it perfectly clear, I’ll name this quadCurve and the cubic one will be cubicCurve

function quadCurve(x0, y0, x1, y1, x2, y2, res) {
  moveTo(x0, y0)
  for (t = res; t < 1; t += res) {
    x, y = quadBezierPoint(x0, y0, x1, y1, x2, y2, t)
    lineTo(x, y)
  }
  lineTo(x2, y2)
}

To make sure we explicitly start and end on the start and end points, we’ll start with an explicit moveTo to the first point and end with an explicit lineTo to the last point. The function takes a res parameter that lets you know how many steps to take along the curve. We’ll start t out equaling res because we’ve already moved to the first point, which is what you’d get if t was 0. In the middle of all that, we just get the point corresponding to the current t and draw a line to it.

Of course, you can make a quadCurveTo method by dropping the first two parameters and the moveTo. This will rely on the user using their own moveTo to specify the starting point of the curve (or continuing it from an existing path). Example of this in use:

canvas(800, 800)
quadCurve(100, 100, 200, 700, 700, 300, 0.01)
stroke()

Which gives us:

If we change the res to something larger like 0.1, we see that things get a bit chunky:

So here you’ll have to experiment with a resolution value that works well. Given that the built-in Bézier methods already figure out a good resolution for you, this curve method has questionable value. But, it got us to write the quadBezierPoint function, which has a LOT of value, as we’ll see.

One thing that the point function can do that the built in methods can’t is animation. For this section, like I did in previous chapters, I’m going to assume you have or can make some kind of function that runs repeatedly and can create animations. I’m going to call it loop. What I’m going to do here is rather than drawing the curve from a t of 0 to 1, I’ll have it go from 0 to a value finalT that will change over time.

canvas(400, 400)
x0 = 50
y0 = 50
x1 = 150
y1 = 360
x2 = 360
y2 = 150
finalT = 0
dt = 0.01
res = 0.025

function loop() {
  clearCanvas()
  moveTo(x0, y0)
  for (t = res; t < finalT; t += res) {
    x, y = quadBezierPoint(x0, y0, x1, y1, x2, y2, t)
    lineto(x, y)
  }
  stroke()

  // add to finalT
  finalT += dt

  // if we go past 1, turn it around
  if (finalT > 1) {
    finalT = 1
    dt = -dt
  } else if (finalT < 0) {
    // if we go past 0, turn it back
    finalT = 0
    dt = -dt
  }
}

And this should give you an animation that looks something like this:

Here, the for loop is going from res to finalT so it doesn’t draw the entire curve (unless finalT equals 1). Then we change finalT by adding dt to it. This brings finalT closer and closer to 1, so the curve is drawn more and more fully. Eventually finalT will go beyond 1 so we set it back to 1 and make dt negative, which reverses the whole process until finalT goes below 0, where we bounce it back the other way.

Rather than just drawing a line, we can animate an object along a Bézier path now! Here’s the code for that. It should be pretty clear. I’ll just include the loop function. The rest should be the same.

function loop() {
  clearCanvas()

  x, y = quadBezierPoint(x0, y0, x1, y1, x2, y2, finalT)
  circle(x, y, 10)
  fill()

  // no changes beyond here...
  // add to finalT
  finalT += dt

  // if we go past 1, turn it around
  if (finalT > 1) {
    finalT = 1
    dt = -dt
  } else if (finalT < 0) {
    // if we go past 0, turn it back
    finalT = 0
    dt = -dt
  }
}

Now we’re just getting the x, y point for the current value of finalT and drawing a circle there. This assumes you have a circle drawing function. You can use the one we created in Chapter 3 if you need one.

In this example, I drew a light line for the same quadratic curve using the built-in method of my api, just to show that we’re on track with the standard definitions of these things.

OK, let’s take a break here and jump over to cubic curves

Cubic

Pretty much everything I said above for quadratic curves is going to apply to cubics. It’s just a different formula – a bit more complicated. Here is is for one dimension:

x = (1 - t) * (1 - t) * (1 - t) * x0 + 3 * (1 - t) * (1 - t) * t * x1 + 3 * (1 - t) * t * t * x2 + t * t * t * x3

And the 2D function:

function cubicBezierPoint(x0, y0, x1, y1, x2, y2, x3, y3, t) {
  x = (1 - t) * (1 - t) * (1 - t) * x0 + 3 * (1 - t) * (1 - t) * t * x1 + 3 * (1 - t) * t * t * x2 + t * t * t * x3
  y = (1 - t) * (1 - t) * (1 - t) * y0 + 3 * (1 - t) * (1 - t) * t * y1 + 3 * (1 - t) * t * t * y2 + t * t * t * y3
  return x, y
}

Yikes! That’s a mess. Let’s clean it up again by factoring out the 1 - ts:

function cubicBezierPoint(x0, y0, x1, y1, x2, y2, x3, y3, t) {
  m = 1 - t
  x = m * m * m * x0 + 3 * m * m * t * x1 + 3 * m * t * t * x2 + t * t * t * x3
  y = m * m * m * y0 + 3 * m * m * t * y1 + 3 * m * t * t * y2 + t * t * t * y3
  return x, y
}

That’s a little better. One more step to clean it up:

function cubicBezierPoint(x0, y0, x1, y1, x2, y2, x3, y3, t) {
  m = 1 - t
  a = m * m * m
  b = 3 * m * m * t
  c = 3 * m * t * t
  d = t * t * t
  x = a * x0 + b * x1 + c * x2 + d * x3
  y = a * y0 + b * y1 + c * y2 + d * y3
  return x, y
}

Much better!

Now we can make a cubicCurve function.

function cubicCurve(x0, y0, x1, y1, x2, y2, x3, y3, res) {
  moveTo(x0, y0)
  for (t = res; t < 1; t += res) {
    x, y = cubicBezierPoint(x0, y0, x1, y1, x2, y2, x3, y3, t)
    lineTo(x, y)
  }
  lineTo(x2, y2)
}

That was easy. No explanation or example needed I think.

Now for your assignment: adjust the animations we did above to work for cubic curves. It’s really just a matter of adding a new x3, y3 point and calling the new function.

That’s the basic code for implementing Bézier curves and paths. But there are a few more neat tricks for you.

Drawing Through a Point

At some point, everyone who starts coding Bézier curves is going to say:

This is neat, but I want it to go THROUGH the control point(s).

Me – some time around the year 2000.

Well, we can do that! It’s pretty easy for quadratic curves. What you need to do is create another control point that will pull the curve even further out so that it just goes through the original control point. And that new point is pretty simple to calculate. Where the points are x0, y0, x1, y1, x2, y1, the new control point will be:

x = x1 * 2 - x0 / 2 - x2 / 2
y = y1 * 2 - x0 / 2 - x2 / 2

Now we can make a function, let’s call it quadCurveThrough that implements this behavior. It just gets this new point and uses the built-in function to draw the curve. I’m going to postulate that’s called quadraticCurveTo on your system, but it might be something else.

function quadCurveThrough(x0, y0, x1, y1, x2, y2) {
  xc = x1 * 2 - x0 / 2 - x2 / 2
  yc = y1 * 2 - y0 / 2 - y2 / 2
  moveTo(x0, y0)
  quadraticCurveTo(xc, yc, x2, y2)
}

Here I’ve drawn a regular quadratic curve in red and one using this function in blue. And I drew in the points to prove that it does what I claim it does!

Your next question is how to do the same thing for cubic curves. I don’t have an answer for that one yet. But I will keep digging. I guess there’s a chance that someone will comment the answer here, or tell me it’s not possible. 🙂

Piece-wise Quadratic Bézier Curves

The other question people commonly ask is:

How do I make a Bézier curve with N control points (where N is 3 to infinity)?

Also me around the same time as earlier question

As mentioned earlier, this is mathematically possible, but it gets prohibitively expensive when you move past cubic. That’s why you’ll probably never see quartic or quintic Bézier curve functions. But it’s still a very useful thing to have a smooth curve with an arbitrary number of control points. And of course you’ve seen this kind of thing any time you’ve used a pen tool in a drawing program.

In the splines video above (second one by Freya), she shows making a longer curve by piecing together multiple cubic Bézier curves.

https://www.youtube.com/watch?v=jvPPXbo87ds: Coding Curves 08: Bézier Curves

These are sometimes called piecewise Bézier curves. I’m going to show you something a bit simpler, using only quadratic curves. It’s not too hard to implement and it supports any number of control points you want to throw at it. I’ll even show you a version that creates a closed loop.

This technique is actually covered in depth in the second video I posted above (by me):

https://www.youtube.com/watch?v=2hL1LGMVnVM: Coding Curves 08: Bézier Curves

So I’m not going to do too deep a dive here, but I’ll go over the basics and give you some code and examples.

The basic principle is you’re going to first create a new point that is mid-way between p0 and p1. Call that pA. And another that’s between p1 and p2. Call that pB. Draw a line from p0 to pA, then draw a quadratic curve using pA, p1 and pB.

Then you’ll get midpoint from p2 and p3, called pC and make a curve from pB through p2 and ending at pC.

You’ll continue that until the end where you draw a curve from the second-to-last midpoint, through the second-to-last point, ending at the last midpoint. Then finally a line from the last midpoint to the last point.

And here’s the curve:

The code for this can get a little tricky, but having worked through it a few times, I’m pretty happy with a method that looks like the following. Note, that due to the potentially large number of parameters that will be passed in, it really helps to have some kind of point object. Whether that’s a class, structure, or generic object with x and y properties.. up to you and your language. This method will take an array of these point objects. The code assumes the array has a length property, but there might be something different in your language, like a len method.

function multiCurve(points) {
  // line from the first point to the first midpoint.
  moveTo(points[0].x, points[0].y)
  midX = (points[0].x + points[1].x) / 2
  midY = (points[0].y + points[1].y) / 2
  lineTo(midX, midY)

  // loop through the points array, starting at index 1
  // and ending at the second-to-last point
  for (i = 1; i < points.length - 1; i++) {
    // find the next two points and their midpoint
    p0 = points[i]
    p1 = points[i+1]
    midX = (p0.x + p1.x) / 2
    midY = (p0.y + p1.y) / 2

    // curve through next point to midpoint
    quadraticCurveTo(p0.x, p0.y, midX, midY)
  }

  // we'll be left at the last midpoint
  // draw line to last point
  p = points[points.length - 1]
  lineTo(p.x, p.y)
}

That method seems long, but I added a lot of comments to each part.

For an example, I generated a half dozen random points. I don’t know how you’d do that on your system, so I’m going to say we have a function randomPoint(xmin, ymin, xmax, ymax). (Actually I do have such a method in my own library!) Once you have your points array, you just pass that array to your new function and stroke the resulting path:

context(800, 800)
points = []
for (i = 0; i < 6; i++) {
  points.push(randomPoint(0, 0, 800, 800))
}

multiCurve(points)
stroke()

The glorious result:

Quite nice. I also drew all the points to give a bit more context on why the curve looks like it does.

Closed Curves

The final thing in this section will be to alter the method to allow for a closed loop. Mainly that’s going to be getting rid of the starting and final line segments, and figuring out the curve from the end of the curve back to the beginning.

function multiLoop(points) {
  // find the first midpoint and move to it.
  // we'll keep this around for later
  midX0 = (points[0].x + points[1].x) / 2
  midY0 = (points[0].y + points[1].y) / 2
  moveTo(midX0, midY0)

  // the for loop doesn't change
  for (i = 1; i < points.length - 1; i++) {
    p0 = points[i]
    p1 = points[i+1]
    midX = (p0.x + p1.x) / 2
    midY = (p0.y + p1.y) / 2
    quadraticCurveTo(p0.x, p0.y, midX, midY)
  }

  // we'll be left at the last midpoint
  // find the midpoint between the last and first points
  p = points[points.length - 1]
  midX1 = (p.y + points[0].x) / 2
  midY1 = (p.y + points[0].y) / 2

  // curve through the last point to that new midpoint
  quadraticCurveTo(p.x, p.y, midX1, midY1)

  // then curve through the first point to that first midpoint you saved earlier
  quadraticCurveTo(points[0].x, points[0].y, midX0, midY0)
}

To explain a bit more here’s the first step and the for loop:

We move to the first midpoint, then loop through the rest, finding midpoints and doing quadratic curves. This leaves us at the last midpoint. Then…

We find the midpoint between the first and last points, and execute the last two curves to close up the shape. The following image is made with the same setup as the last one, but calling multiLoop instead of multiCurve (and different random points).

These are two of my favorite functions, and I’m happy to share them with you.

Even Distribution

The final trick I want to share has to do with evenly distributing objects on a quadratic curve. One use case for this is when you want to have text follow a curve. You want to be able to space the letters evenly on the curve. You’ll also want to rotate them to follow the curve, but that’s beyond the scope of this article.

At first this might seem like a trivial problem. You have your t value that you use to divide up the curve. If you want, say, 20 object spaced out on the curve, just have them 0.05 apart. 20 x 0.05 = 1.0. Work done. Well, let’s try it.

canvas(800, 800)

x0 = 100
y0 = 700
x1 = 100
y1 = 100
x2 = 700
y2 = 400

moveTo(x0, y0)
quadraticCurveTo(x1, y1, x2, y2)
stroke()

// 20 evenly spaced t values (21 counting the end one)
for (t = 0; t <= 1; t += 0.05) {
  x, y = quadBezierPoint(x0, y0, x1, y1, x2, y2, t)
  circle(x, y, 6)
  fill()
}

Here’s what that gives us.

Not evenly spaced at all. The ones near the ends are spaced out and in the middle they are closer together. That’s just the way it is with Bézier curves. So, we have to figure out a way to get those points evenly spaced.

Sadly, there’s not a very easy way to do this. I’m going to give you a horribly un-optimized, brute force way to get decent results, and a couple of hints on how to make it better.

So, to get evenly spaced points along a curve, it makes sense that you need to know the length of the curve. If the length is 200 pixels, and you want 20 points, you put one point every 10 pixels along the length of the curve.

Surprisingly, there is no simple formula to get the length of a Bézier curve. But we can get pretty close by sampling a bunch of points along the curve and getting the distance between each pair. This would look something like this:

function quadBezLength(x0, y0, x1, y1, x2, y2, count) {
  length = 0.0
  dt = 1.0 / count
  x, y = x0, y0
  for (t = dt; t < 1; t += dt) {
    xn, yn = quadBezierPoint(x0, y0, x1, y1, x2, y2, t)
    length += distance(x, y, xn, yn)
    x, y = xn, yn
  }
  length == distance(x, y, x2, y2)
  return length
}

Here, count is how many samples we want to take. The more samples, the more accurate we’ll be.

Then, dt is the amount to increase t by as we loop through the curve.

We keep track of the last point, x, y, which will start as x0, y0. Then we loop through the curve getting each new point, xn, yn and finding the distance between the last point and the new point, then making the new point the last point. I’m not going to show you how to find the distance between two points, just assuming that you have a function for that. You add that distance to the accumulating length.

Then you do one last length addition for x2, y2. Then return the length.

Make sure that all makes sense first, because I’m going to throw something else in there.

It’s going to be very useful to keep track of what the length was at each point as we work through the curve. So we’re going to store each successive value in an array. And rather than returning the total length, we’ll return the array.

function quadBezLengths(x0, y0, x1, y1, x2, y2, count) {
  lengths = []
  length = 0.0
  dt = 1.0 / count
  x, y = x0, y0
  for (t = dt; t < 1; t += dt) {
    xn, yn = quadBezierPoint(x0, y0, x1, y1, x2, y2, t)
    length += distance(x, y, xn, yn)
    lengths.push(length)
    x, y = xn, yn
  }
  length == distance(x, y, x2, y2)
  lengths.push(length)
  return lengths
}

Now the full length of the curve is in the last element, but we have a whole bunch of other sub-lengths too. Here’s what we do.

count = 500
lengths = quadBezLengths(x0, y0, x1, y1, x2, y2, count)
length = lengths[count-1]

for (i = 0.0; i <= 1; i += 0.05) {
  // the length of the curve up to the next point
  targetLength = i * length

  // loop through the array until the length is higher than the target length
  for (j = 0; j < count; j++) {
    if (lengths[j] > targetLength) {
      // t is now the percentage of the way we got through the array.
      // this is the t value we need to get the next point
      t = j / count

      // get the point and draw the next circle.
      x, y = quadBezierPoint(x0, y0, x1, y1, x2, y2, t)
      circle(x, y, 6)
      fill()
      break
    }
  }
}

OK, a bit complex, but let’s go through it. We get the lengths using a count of 500, and capture the total length.

The we loop through from 0 to 1 by 0.05, like we were doing before. But rather than using that as the t value of the Bézier curve, we use it to find a fraction of the length of the curve. Say the curve was 500 pixels long and i was 0.5, then the target length we are looking for is 250 for the next point.

Now we loop through the array with j and get the length values until we go above our target length. If we divide j by count, we wind up with the t value that created this particular length. We plug that back into the Bézier point function to get the next point and draw it. We also want to break out of the inner loop at that point, since we are done and can move on to the next point.

This gives us the following:

We missed the last point (because we never went beyond that length), but we could just draw another point at x2, y2. This is pretty close to evenly spaced. The higher the value you use for count, the more accurate it will be. If we move count down to 100, we can see it’s off:

Now, there is a LOT wrong with this code. Mostly in its optimization.

First of all, rather than looping through from 0 on each point, we could use a binary search.

Secondly, we have to add a lot of points to get any kind of accuracy. Because we are just grabbing the last point we find in that inner loop. Instead of taking a predefined point, which we know is a bit too high, we could interpolate between that point and the previous point.

For example, say our target length was 150. And at index 87 we got a length of 160. We look back at index 86 and we find 140. OK, so we want a value half way in between 86 and 87. So instead of calculating t as 87 / count, or even 86 / count, we interpolate 50% and say 86.5 / count. It still won’t be perfect, but you can use a lower count and still get good results.

I’m going to leave all of that as an exercise for you.

If you want more info on this technique, and a fuller explanation, check out this site:

http://www.planetclegg.com/projects/WarpingTextToSplines.html

Summary

So there, you go, a few basics, a few tips, a few tricks. Until next time…

Goodbye Twitter

misc

I’ll keep this brief. I’m leaving Twitter after 16 years. A tough decision. 8000 followers. Lots of history. It was an overwhelmingly positive experience. But the platform is in a very different place now and I just don’t want to have any connection to it going forward. I’ll disable my account some time on December 13, 2022 probably.

You can now find me over at Mastodon at bit101 (@bit101@mstdn.social) – Mastodon 🐘

I’ve flirted with Mastodon for several years now. There is now a critical mass of people I know there. I’m having a great time there and it feels very much like the early days of Twitter. Good conversation, good creativity, low drama.

I met so many people and maintained so many good relationships on Twitter. I hope to continue to stay in touch with a lot of you, either here, on Mastodon, or on other avenues.

ANSI Escape Codes

misc

Yesterday I created a little Go library to manage ANSI escape codes in Go.

I’ve been working on a command line app that will have a user selecting items from a list and asking a few questions in order to configure how the app runs. I realized that a bit of color would add a ton of context and make the experience much easier to navigate.

It turns out that there are quite a few libraries that do this kind of thing. Initially I used https://github.com/fatih/color which is pretty nice. But as always there were one or two things that didn’t work exactly the way I wanted them to. I started looking into how colors are defined in shells and just kind of wound up going down a rabbit hole on the subject.

I started just making a few functions that did the few things I wanted to do. These actually were sufficient to replace all of what I was doing with the library. But I kept working on it and kept seeing ways to improve it. And I was learning a lot and having fun. I started in the morning and banged on it off and on all day

Basics

ANSI escape codes can do all kinds of things in the terminal. You’re already familiar with a few, like \n for a new line, and \t for a tab character, and maybe \r for a carriage return (moves to the beginning of the current line without adding a new line).

Most people take a deep dive into escape codes when, like me, they want to set colors in the terminal. I’ve messed with this before when setting up my custom prompt a few times. But “promptly” forget about how it works each time. (Come on, that was good!)

But they are also useful for setting properties like bold, underline, reversed, moving to different locations in the terminal, clearing existing text from a line or the whole screen.

Most of these settings start by printing the escape code character to the terminal. That escape code is 27 in decimal, but you need to format it as an escape character. This can be done in a few different ways.

The most common way is octal:

\033

But some people prefer hex:

\x1b

If you’re really into unicode, you can even go with:

\u001b

I prefer \033 so that’s what I’ve used throughout.

After the escape code, you need to print an opening square bracket for most of the commands we’ll be using. This is the Control Sequence Introducer or CSI.

\033[

Now you’re set up to enter a code that actually does something.

Colors

Basic colors are defined by a number between 30 and 37, followed by the letter “m”.

Some references will tell you that you need two numbers, separated by a semicolon, and followed by the letter “m”. The first number can be 0 or 1 and specifies the brightness/boldness of the color. The second number is a number from 30 to 37 inclusive. This gives you eight base colors with two shades for a total of 16.

This is actually pretty inaccurate and I’ll cover the truth below, but for now let’s just look at the eight base colors.

To set a regular red color, you’d do this:

\033[31m

Try it in a terminal like so:

echo "\033[31mHello, world, I am red."

If this doesn’t work for you, you might have to pass -e to the echo command to enable it to interpret the backslash escape character.

echo -e "\033[31mHello, world, I am red."

These colors may look slightly different depending on your OS, terminal emulator and colorscheme in use.

Here are the color codes;

30: Black
31: Red
32: Green
33: Yellow
34: Blue
35: Magenta
36: Cyan
37: White

The Shocking Truth about that Leading 0/1

First, let’s learn about another ANSI code – bold. The code for bold is just 1. And if you want to turn it off, or explicitly say not bold, use 22.

echo "\033[1mThis is bold!"

You can even insert the sequence in the middle of a string and turn it off again later.

echo "The word \033[1mbold\033[22m is in bold"

It’s subtle there, but yeah.

We can combine multiple sequences into one. Just set the color code, a semicolon, and the bold code, all followed by the “m”. For example, we can add a color and the bold attribute. Let’s compare “regular yellow” with “yellow plus bold”.

echo "\033[33myellow"
echo "\033[33;1mbold yellow"

So as I said, some references tell you you need the 0 or 1, followed by the color code to fully define a color. Like this:

Black        0;30     Dark Gray     1;30
Red          0;31     Light Red     1;31
Green        0;32     Light Green   1;32
Brown/Orange 0;33     Yellow        1;33
Blue         0;34     Light Blue    1;34
Purple       0;35     Light Purple  1;35
Cyan         0;36     Light Cyan    1;36
Light Gray   0;37     White         1;37

This is wrong and misleading. There are eight basic colors and 1 is the code to print in bold. Period. Yes, unbolded yellow can be pretty dim and look more orange or brown, but these are non-standard, random names that just confuse things.

So what about 0? Well it’s not the “not bold” code. In fact, we already saw that 22 is the code to undo bold.

In fact, 0 is the reset code. It resets all styles. So it’s actually pretty good to have it in there as a first code, especially when you are trying to create a new style when other styles might already be in play.

But thinking that the first code should be “0 or 1” is very misleading and can lead to confusion. Here’s a use case:

Say I wanted some text in regular green, underlined and then the more text in bold red – not underlined. If I’m fixated on “0 or 1”, then I’ll do something like this (4 is the code for underline):

echo "\033[0;32;4munderlined regular green \033[1;31mbold red"

But now the red is still underlined. If I change the last 1 to a 0, then I’ll get rid of the underline, but I’ll lose the bold. I actually need both! And there’s no problem with doing that.

echo "\033[0;32;4munderlined regular green \033[0;1;31mbold red"

In fact, you could move the 1 later, like this:

echo "\033[0;32;4munderlined regular green \033[0;31;1mbold red"

The first version is saying “clear it, then make it bold and red” and the second one is saying “clear it, then make it red and bold”. Same thing.

Thinking that colors are a two-part code with a leading 0 or 1 is just incorrect. Saying you have to prefix a 0 or 1 is literally saying, “reset all styles OR add a bold style to whatever style is there already.” Illogical.

It took me a long time to work through the logic of all this, but now it makes a lot more sense. Hopefully this helps you down the line.

Actual Bright Colors

There’s one more color / shading alternative, which is another set of actual “bright” colors from 90 to 97. These are brighter than the regular colors, but don’t give you quite the brightness as the bold versions.

Below you can see 36m, 96m, 1;36m and 1;96m.

A subtle difference, but good to know. (Actually I don’t see any difference in the last two, but maybe you do.)

Background Colors

You can also use ANSI escapes to set background colors. these again follow the same sequence but go from 40 to 47.

echo "\033[41mRed background"

Now you can combine a background and foreground color:

echo "\033[0;1;32;41mGreen on red, my favorite"

There are other codes for making text more dim, or italic or strikethrough, but these have much less support in terminal emulators than the ones I’ve mentioned.

And if you have a supported terminal, you can specify up to 256 colors with a bit different syntax that I’m not going to cover here because it’s just beyond what I need.

There’s a whole lot of other stuff you can do with these codes, including moving the cursor up or down or left or right or to a specific row and column, and clearing part or all of a line or part or all of the terminal window.

This page is one of the better one-stop references I’ve found:

https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797

The library!

So anyway, back to that library I created… 🙂

It just incorporates all of this into a Go module giving you functions you can call rather than trying to remember all those codes.

It’s here: https://github.com/bit101/go-ansi

Described pretty well there, but basically you can do things like:

ansi.SetFg(ansi.Red)
ansi.SetBg(ansi.Black)
ansi.SetBold(true)
ansi.SetUnderline(false)
ansi.SetReversed(false)

fmt.Println("Hello, world!")

And this will print in bold red on a black background. One of the cool things about using these sequences in code is that they are “sticky”, i.e. once you set some of these properties, they apply to anything else you print to the console until you change or reset them. This is unlike using echo in the terminal itself, where each escape is one-shot.

In addition to these sticky property settings, I also created a few print helper functions that mirror the built in Go print functions: ansi.Print, ansi.Printf, and ansi.Println. These just add an ANSI color constant as a first argument.

ansi.Println(ansi.Red, "this will be red)"

Like echo, these are one-shot functions, which is useful when you want to print one message in a color and not have to worry about resetting things back to default.

It also has functions for several of those cursor movement and screen clearing codes.

As I said there are plenty of other libs out there that do similar things, but I built this to work just the way I want it to. So I’m keeping it!

My Raytracing Journey

misc

Anyone who follows me on twitter has seen what I’ve been up to in the last month and a half or so. But to hop on a meme from last year…

How it started:

September 18, 2022

How it’s going:

October 23, 2022

Not bad for 35 days of nights and weekends, if I do say so myself, but let’s go back to the start and take an image-filled journey.

It started in a book store

My wife, daughter and I are all book addicts. Our idea of someplace fun to go on the weekend is Barnes and Noble, which is about a ten minute drive down the highway. We were there one Saturday and I saw this book and started looking through it:

The first half of the book is about ray tracing and the second half is about rasterized 3D. The content looked really accessible and even just skimming through it, it seemed like something I could follow along with and code. I recently got an oreilly.com subscription, so I was able to access the book there, and had the first image you see above rendered in no time. And I understood what was going on with the code. I was hooked!

What is Raytracing?

I’m absolutely not going to try to teach you raytracing, but I’ll try to give you a 10,000 foot view.

The two major schools in 3D rendering are ray tracing and rasterization. Rasterization usually involved creating a bunch of triangles or other polygons out of a bunch of 3D points, figuring out how to fill in those triangles and filling them in. I’ve coded that kind of thing from scratch multiple times at different levels of thoroughness over the last 20 years.

Raytracing though, is something I’ve never touched. It involves making a model of 3D primitives and materials and lights, and then shooting out a ray through every pixel in the image, seeing what that ray hits, if anything, and coloring it accordingly.

A good analogy from the book is if you held a screen out in front of you and looked through each hole in the screen from a fixed viewpoint. Left to right, top to bottom. When you looked through that one hole, what did you see? Color a corresponding point on a canvas with that color paint. You might see nothing but sky in the top row of the screen, so you’d be doing a lot of blue points on the canvas. Eventually you’d hit some clouds or trees and do some white or green dots. Down lower you might hit other objects – buildings, a road, grass, etc. When you worked through all the holes in the screen, you’d have a completed painting. If you understood that, you understand the first level of raytracing.

So you model three spheres and say they are red, green and blue. You shoot a ray from a fixed “camera” point, through each pixel position in your image. Does it hit one of the spheres? If so, color that pixel with the color of the sphere it hit. If not, color it black. That’s exactly what you have here:

A ray is a mathematical construct consisting of a 3D point (x, y, z) and a direction – a 3D vector (also x, y, z). So the first step is to get or create a library of functions for manipulating 3D points and vectors and eventually matrices. There’s a fairly simple formula for finding out if a ray intersected a sphere. It will return 0, 1 or 2 points of intersection. Zero means it missed entirely, one means it just skimmed the surface, and two means it hit the sphere, entered, and exited.

Of course a single ray may hit multiple objects. So the algorithm has to find the first one it hit – the intersection closest to the origin of the ray. But… it’s entirely possible there could be objects behind the camera, so you need to filter those out.

Lighting, shadows, reflection

The first image looks a bit flat, but lighting, shadows and reflection take care of that. Add to your world model one or more lights. There are different types of lights, but point lights have a point and an intensity. The intensity can be a single number, or it could be an RGB value.

When you find your point of intersection for a given pixel, you then need to shoot another ray from that intersection point to each light. Can the ray reach the light without being blocked by another object? If so, what is the angle at which the light is hitting the object at that point. If it’s hitting straight on, that part of the object will be brighter. If it’s hitting at nearly 90 degrees, it’s just barely lighting it.

And that’s just for diffuse material. But that gives you this:

You can tell that my light in this picture is off to the right and a bit higher than the spheres. You’ll also notice that there seems to be a floor here, even though I’ve only mentioned spheres. The trick to that is that the yellow floor i just a very large sphere. But it also illustrates that closest intersection point. For some pixels the ray hits the yellow floor sphere first, so you don’t see the red sphere, but in other areas, it hits the red sphere first, so it blocks out the yellow one.

In order to figure out that light/angle part, you need to know the “normal” of the surface. That’s another ray that shoots out perpendicular to the surface at that point. I knew from previous dabbles in 3D graphics that if you start messing with that normal, it changes how light reacts with the surface. So I took a bit of a diversion and used a Simplex noise algorithm to alter the normal at each point of intersection. I just hacked this together on my own, but I was pretty much on the right track.

But getting back on track, some materials are more shiny and the light that reflects off of them depends on the angle you are looking at them from. So there’s another calculation that takes into account the surface normal, the angle to the light, and the angle to the camera or eye. This gives you specular lighting.

Getting better. But then there are shadows. When you are shooting that ray out from the intersection point to each light, you have to see if it intersects any other object. If so, that light does not affect the color of that pixel.

Here, there are multiple lights, so you see shadows going off in different directions. Already things are starting to look pretty cool.

Finally, reflections. When a ray hits an object, and that object is reflective, it’s going to bounce off and hit some other object, which is going to affect the final colorization of that pixel. It can be confusing because this is all being calculated in reverse of the way light works in the real world. We’re going from the destination and working back to the source.

If you have multiple reflective objects, the light might wind up reflecting back and forth between them for quite a while. This is not only very costly, but it has quickly diminishing returns, so you usually set a limit on how many levels of reflection you want. So now you are figuring out the color of a given pixel by factoring in the surface color, each light and its angle, what kind of material you have, and all possible reflections. Sounds intimidating, but when you figure out each piece one by one, they all fit together way too logically and just work to create something like this:

And that is about as far as I got with the first book. Spheres, lights, shadows, materials, reflections. I could change the size of the spheres and move them around, but couldn’t deform them in any way. Still, with all that, I was able to have a jolly good bit of fun.

Phase 2 – The Next Book

Getting this far took me just about a week. Could have been faster, but every time I coded a new feature I’d spend an hour or several playing with it. I was excited but I needed more than simple spheres. I wanted to mess with those spheres, squish them and stretch them and apply images and patterns and textures to them. I wanted a real floor and cubes and cylinders and cones and whatever else I could get.

The Computer Graphics from Scratch book was great and I highly recommend it if you want a quick jump into the subject. One thing I particularly loved about it is that it wasn’t the kind of book that just dumps a lot of code on you and explains it. It gives you the concepts, the formulas and maybe some pseudocode and it’s up to you to choose a language and figure out the implementation details. I wound up doing mine in Go because its the language I am currently most comfortable with. But I think the author does have some sample code somewhere that is done in JavaScript.

But I was ready for the next part of the journey. So I found my next book:

Oh yes, this is the one! This one goes deep and long and it took me almost four weeks to get through, but I could not put it down. Again, I’d learn something new in the first hour or so of an evening, and spend the rest of the evening messing around with it and rendering all kinds of new things using that concept.

This is honestly probably one of the best written technical books I have ever read. Like the first one, it gives you no source code and is not tied to any language. Again the author provides concepts, algorithms and some pseudocode where needed. But as the cover says, it’s a test driven approach. I cringed at first, but I was so happy for this approach as I got deep into it. For each new concept the author describes what you need to do and then gives you a test spec. Like, “given this set of object with this set of inputs, calling this method should give you these values…” Very often it is as specific as, “the color value should be red: 0.73849, green: 0.29343, blue: 0.53383”. I just made those numbers up, but yeah, it’s like down to 5 digits. I was skeptical when I first saw this. Like no way can you let me choose the language and platform and implementation details and expect that I’m going to be accurate down to 5 digits across three color channels. But goddamn! It was in almost every case. I only saw some slight diversion when I got down into transparency and refraction. And then I was still good down to 4 digits. Any time I was off by more than that, I eventually found a bug in my own code, which, when fixed, brought it back to the expected values. Amazing! These tests caught SOOOOOO many minor bugs that I would have been blissfully ignorant of otherwise. It really sold me on the value of testing graphical code, something I never really considered was possible. Brilliant approach to teaching!

The first few chapters were slow. It was building up that whole library of points and vectors rays and matrices and transformation functions. And then finally the camera and world and spheres and intersections. It wasn’t until Chapter 5 that I could render my first sphere! And I was back to this:

But we move pretty quickly from there to lighting things up:

And then right away into transforming those spheres!

And then into shadows and finally beyond spheres into a real plane object!

Then we got to an exciting part for me: patterns. Algorithmic ways of varying a surfaces. The author explained a few – stripes. checkers and a gradient, but I went off on a wild pattern tangent of my own.

Eventually I got back on track and got back through reflection and then on to transparency with refraction!

The refraction part was the hardest so far. The code itself got pretty involved but beyond that it’s really hard to compose a compelling scene with transparent, refractive objects. It’s way too easy to overdo it and it winds up looking unrealistic. Best used with a light touch.

I took another short diversion into trying to model some simple characters. This one cracked me up.

It wasn’t intended, but it wound up being a dead ringer for this classic:

Finally we got onto new object types. Cubes, cylinders, cones:

And I took some diversions into combining these in interesting ways.

Then we created triangles. And built shapes up from them.

There was a good chunk of that chapter devoted to loading, parsing and rendering object files and smoothing triangles out, etc. This was the one of the few parts of the book I jumped over because I’m not really interested in loading in pre-built models. The other part I jumped over was bounding boxes. This is mostly an optimization technique to limit the number of objects you have to test for collisions. I’ll have to get back to that eventually.

But the next exciting topic was groups and CSG groups – constructive solid geometry. This is where you take two shapes and combine them. The combination can be a union – you get the outline of both shapes, an intersection – you just get the parts of both shapes that overlap, or a difference – the second shape takes a bit out of the first. Although you can only combine two shapes at a time, a CSG group is a shape itself, which can be combined with other shapes, winding up with a binary tree kind of structure that can create some very complex forms.

This is a sphere with another sphere taking a bite out of it, and then punched through with a cylinder. I didn’t spend nearly enough time with this, but will surely do so.

That wrapped up the book, but I continued to explore. I was still intrigued with patterns. A pattern is essentially a function that takes an x, y, z point and returns a color. Hmm… what could we do with that? I know! Fractals!

These are not fractal images mapped onto surfaces. The Mandelbrots and Julias are computed at render time. Very fun.

From there, I started working out image mapping on my own.

I did pretty damn well working image mapping out by myself. *Pats self on back* But it wasn’t perfect. There were some concepts I was missing and things got funky now and then. These images are the ones that worked out well. You won’t see all the ones that were just a mess.

I also started exploring normal perturbation more, with noise and images – normal maps and bump maps.

Again, these look good, but I was missing some concepts.

As I did more research, I eventually discovered that the author of The Ray Tracer Challenge had published a few bonus chapters on his site.

http://raytracerchallenge.com/#bonus

One of these was about texture mapping. This gave me the final pieces that I was missing in image and bump mapping. And I was able to do stuff like this.

Part of that chapter was about cube mapping which was super complex and contained the only actual errors I found in the author’s work. I confirmed it on the books forum site with a few other people who ran into the same issue.

Once you have cube mapping, you can make what’s called a sky box. You make a huge cube and map images to its side. The images are specially warped so that no matter how you view them, you don’t actually see the cube. It just looks like a 3D environment. That’s the image you see at the top of this post.

Here you can see the render minus the skybox:

And here is the skybox by itself:

Though it looks like it could just be a flat background image. I could actually pan around that image and view it from any angle. Note the reflections in the full image, where you can see buildings that are behind the camera reflected in the sphere.

That particular skybox image set was from here:

http://www.humus.name/index.php?page=Textures

And there you can see some live, interactive demos of those skyboxes where you can pan around the image in real time.

The final thing I’ve been working on recently is creating a description format for a full scene. I tried JSON and TOML, but settled on YAML as the best one to handcode a descriptor scene. Now I have an executable file that I just point to a YAML file and it creates the scene, renders it and outputs the image.

Here’s another image using that same skybox with some other objects:

This was rendered completely with this new executable. I only wrote this YAML to describe it:

yokohamabox: &yokohamabox
  - "./yokohama/negz.png"
  - "./yokohama/posz.png"
  - "./yokohama/negx.png"
  - "./yokohama/posx.png"
  - "./yokohama/posy.png"
  - "./yokohama/negy.png"

spherematerial: &spherematerial
  color: [0.9, 0.9, 0.5]
  reflective: 0.9
  specular: 1
  shininess: 100
  diffuse: 0.2

# =====================
# START Shapes
# =====================
shape: &skybox
  kind: "cube"
  transforms:
    - rotateX: -0.1
    - rotateZ: -0.2
    - scale: [100, 100, 100]
  material:
    ambient: 1
    reflective: 0
    diffuse: 0
    pattern:
      kind: "cube_map"
      faceImages: *yokohamabox

sphere1: &sphere1
  kind: "sphere"
  material: *spherematerial
  transforms:
    - scale: [0.5, 0.5, 0.5]

sphere2: &sphere2
  kind: "sphere"
  material: *spherematerial
  transforms:
    - translate: [1, -1, 1]
    - scale: [0.75, 0.75, 0.75]

sphere3: &sphere3
  kind: "sphere"
  material: *spherematerial
  transforms:
    - translate: [-1, 1, 1]

sphere4: &sphere4
  kind: "sphere"
  material: *spherematerial
  transforms:
    - translate: [1.5, 0.5, -0.5]

sphere5: &sphere5
  kind: "sphere"
  material: *spherematerial
  transforms:
    - translate: [-1.5, -0.9, -0.5]
    - scale: [0.5, 0.5, 0.5]
plate: &plate
  kind: "cylinder"
  min: 0
  max: 0.2
  closed: true
  material: *spherematerial
  transforms:
    - rotateX: -0.1
    - rotateZ: -0.2
    - translate: [0, -2.5, 0]
    - scale: [3, 1, 3]
# =====================
# END Shapes
# =====================

world:
  width: 800
  height: 800

camera:
  fov: 0.6
  antialias: "med"
  from: [0, 0.0, -20]
  to: [0, 0, 0]

shapes:
  - *skybox
  - *sphere1
  - *sphere1
  - *sphere2
  - *sphere3
  - *sphere4
  - *sphere5
  - *plate

One other thing I worked on was antialiasing. The way this is done is instead of just getting the color of a pixel with a single ray, you take multiple samples around fractional parts of that pixel. Some references say up to 100 samples per pixel and then average them. I’ve found that’s way too many. Actually 16 looks pretty good – it makes a HUGE difference in quality. I can’t see any difference in quality if I go past 64 samples though. But it might be different for high res images.

The Future

After 5 solid weeks of working on this in my every spare moment, I needed to step back a bit and breathe. Which for me, meant creating a vim plugin. 🙂 But I’ll be back to this before long. There is still a lot to explore in this realm.