← Back to Kevin's newslettersPublished: 2025 Oct 11

Hi friends!

I had an absolute blast in New York City visiting all of y'all who replied to my last email. Shout out to those who offered to host me, even after I typo’d my dates and suggested I’d be in town for a month rather than a week — true Internet friendship right there!

NYC has a creative energy and depth that I haven’t felt elsewhere — London and Taipei by comparison have much better transit infrastructure (and aren’t as deafeningly loud), but New York definitely feels to me more like the “capital of the world” with all the mixing of social/economic classes, variety of national backgrounds, and enormous variety of snacks.

I also love how New Yorkers comment out loud and otherwise interact with strangers to navigate public situations together.

A simple constraint language

In between hanging with friends in New York, I found a few hours for programming and knocked out a new codeCAD prototype. So far this one is more of a Clojure library rather than full interpreter/language. The key idea is to lazily solve numeric variables only when the ground value is actually required (i.e., when calling OpenCascade C++ code that doesn’t know anything about constraints).

This allows one to use numeric variables and constraints fluidly across functions, with parameters acting as “in/out” variables.

Here’s an example (hypothetical syntax) showing how the relationship between a circle’s radius and diameter could be encapsulated within the circle function:

(defn circle [center: Point
              radius: Length = 1
              diameter: Length = 2]

  (constraint (= radius (* 0.5 diameter)))

  ;; imagine some call here that actually draws the circle
  ;; and so requires radius and diameter to be solved...
)

some-point = [0, 0]

;; numeric variable, initial value of 5
dia = ?5

(circle some-point
        :radius     5
        :diameter dia)

;; now when you use the dia variable, it's going to always be 10
;; This is forced by the circle fn constraint and radius argument
dia ;; => 10

My hypothesis is that this might be very useful for all sorts of reusable geometric stuff. We’ll see how that works out as I keep building it out and developing my own lil’ OpenSCAD-like system.

Bed completed

Back in July we discussed flatpack bed ideas, and I’m happy to report that I’ve completed a first prototype:

For the past few weeks it has successfully met the design objectives of:

  1. lifting the mattress off the floor
  2. creating a ton space where we can hide boxes

I’m pretty chuffed with how it looks, too. I wish I could take credit for the wild striping pattern, but that wasn’t my idea — one sheet just arrived like that (apparently within spec for CP/CP birch plywood) and we decided to roll with it.

The bed design is simple, with symmetric headboard/footboard panels joined to rails with M6 screws / threaded inserts and some metal dowels for additional shear support and to ease assembly:

I designed for the 180x200 cm Ikea mattress we already had, and the frame is pretty much all Ikea internally:

The latter parts aren’t available for purchase, but Ikea offers spare parts — and I can truthfully say that I’ve lost parts of the Malm bed I purchased in my 20’s.

Ikea spare parts order confirmation

Everything was cut out on my friend’s CNC:

This took two days, as we spent most of the first day debugging why his CNC couldn’t cut reasonable circles. This turned out to be backlash from a loose X-axis pinion/rack — I’m now two-for-two diagnosing this exact problem on the CNC routers I’ve cajoled my way into operating here in Amsterdam. Weird.

The pieces were then cleaned up and the edges rounded over using Bosch’s adorable trim router (highly recommended) and the MFT/Paulk-style workbench I designed and had CNC cut at the same time:

(The bed has MFT-style 20mm holes on 96mm centers, since I figured that if something went wrong during the CNC operations, I might as well end up an extra workbench top.)

Each leg is a pair of panels, with the inside one cut short for the mattress. The panels joined through the headboard/footboard with, uh, 3D-printed spacers and tube:

So far it seems to be holding up just fine; no idea how to delegate credit between PCTG’s stronger-than-PLA layer adhesion, the bearing friction of the spacers, and the axial metal screw within the tubes.

On the front of the legs, the tubes are hidden by brass cabinet pulls (which also serve as the nut into which the axial screw clamps the assembly tight).

Next I expect we’ll build some attachments for the bed like a large padded headboard, side table cup holders, plant stands, jacket hangers, etc.

Fan troubles

I used my powered air respirator while finishing the bed, but I found it a bit stuffy and so I’ve been trying to increase the airflow. I don’t have a great way of quantifying the airflow directly, so as a proxy I’ve used an FNB58 inline USBC tester to measure the power consumption of the fan running at full power for one minute.

Here are the results:

BFB1012H  0.214 Wh unimpeded
          0.118 Wh filter and hose

BFB1012UH Unable to run, draws too much current from powerbank even at lowest setting

BFB1012VH 0.213 Wh unimpeded
          0.091 Wh filter and hose
          0.101 Wh adapter (no filter) and hose
          0.106 Wh dual adapter (no filters) and hose
          0.113 Wh dual adapter but no hose.
          0.117 Wh hose only

I started with the “H” fan, but it felt stuffy so I bought the “UH” variant based on the AliExpress listing’s description: “super violent turbo blower”. Unfortunately, this turned out to draw too much current: nominally it’s 6A at 12 V => 72 W, and even at the lowest PWM speed it still triggers some kind of overcurrent shutdown mode in my USB-C power bank (rated at 22.5W, oops).

So then I ordered the “VH”, but from what I can tell it’s no better than the original “H”.

It seems like the airflow restrictions caused by the filter adapter and/or hose limit the fan to about half the power consumption of the unencumbered fan, which presumably means the airflow is substantially less than the rated capacity.

What should I try next?

If you know about moving air, I’d love to hear from you!

My RSS feed

Did you know my website has an RSS feed?

My friend Ivan reported that occasionally the items would all show up as “unread” in his RSS reader. I’ve dug into the issue, which turned out to be related to deploying my website/newsletter from different timezones — the static site generator I use calls Ruby’s Time.parse method, which always parses into a local datetime. Luckily this carefree, just-do-something dynamic language attitude also makes for an easy fix:

# from https://github.com/nanoc/nanoc/blob/1955f7c0bc86de9ad51b4bb62530754c65bc9e61/nanoc/lib/nanoc/helpers/blogging.rb#L250-L263
# modified so that the date is always assumed to be UTC midnight, so that published item IDs don't change as my laptop changes time zones while I'm traveling.
module Nanoc::Helpers
  module Blogging
    def attribute_to_time(arg)
      t = Time.parse(arg)
      Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, "+00:00")
    end
  end
end

if "4.11.6" != Gem.loaded_specs["nanoc"].version.to_s
  throw "Nanoc version changed, check that feed id duck punching works."
end

Misc. stuff