weeknotes: stained glass, livecoding, lazy eval

2025-07-13

Computer-free stained glass

I made my first stained glass!

I reused the form of a sculpture I made at ITP Camp last year. Drawing on crystallography, it’s the space group I always return to (it’s the same as quartz!). The particular shape is simple to remember: you take a cube, and then slice off a corner from three specific edges.

To create this, you create six pieces that are a rectangle with a corner cut off, and three longer rectangles for the sliced part.

After reading By Hand and Eye last week, I wanted to see if I could puzzle out a design with a compass and straightedge, and I did it! I chose to sprinkle some golden ratio in it, and even added a second color to the truncated faces.

Anisotropic lace

I finally sorted through the last bit of triangulation and got my first system to work! I started experimenting with textures over the new texture.

textured lace

Phew, that was a few weeks of work, and now I feel like I need a bit of distance.

Livecoding

I had a few projects this week that were closer to livecoding, where I piece together a system and then explore it for a while. It was refreshing! It was a nice reminder of the kinds of experiences I want to build, where it feels more like noodling on a musical instrument than endlessly debugging.

Rust ramblings, LazyEval

One of the core parts of my code is the “livecoding” thing, where you can use expressions to represent numeric values. In a previous optimization thing, all of these values were computed once per frame and cached. This sped everything up a surprising amount!

I have a special type called LazyNodeF32, which waits for your code to evaluate. The bird model used this for the wing thicknesses and such are represented. I was surprised at how slow these were: even the wireframe version of the bird started to sink below 30fps.

But at 10 pm one night, I took a stab at optimizing (it is tradition. I wrote a lot of the original mind-bending unitcell code when I was home sick with COVID) I ran flamegraph and realized a big part of the cost was just cloning anything with a LazyNodeF32 in my normal code, e.g. I created the full list of feathers and then split them up into groups to position them on the wing. In my head, it was just little equations being passed around, but I forgot that they were carrying around their full context, too.

I’d tried to do this with references before, but the mess of updating lifetimes everywhere and not having a place to store the owned value led to me reverting to that. I’ve been becoming more comfortable with Rc, so I wanted to try that out. It turned out I needed Arc because I sent some read-only version to a thread for some features. And that was enough to bump the wireframe back up to 60 fps, yay.

Lazy nodes were added in for cases where I couldn’t think of a way to do it without it: the equations need access to their current position, but their current position was computed in custom code based on where other items had been placed. The Lazy nodes were nice at the time, but it has been breaking a few other assumptions, like lerpable (interpolating between changes). It would be nice if, like unitcells, their custom code is all finished evaluated during the “one time per frame,” so that I only work with the evaluated values in the systems.

Reading

I read about half of Richard Williams’ Animator’s Survival Kit.

It gave me a lot to think about: It’s a good reminder that animation isn’t just interpolating between two points. It’s also more than easing functions!

I also started to read When the Machine Made Art about computer art in 1960s-1980s.