25 years of design, photography, video production, and web development behind every build.
Sites that do the work.
Clean functional websites that work for you and make you look good.
The lab.
Hardware, software, firmware. Things to solve little problems and make people happy.
Are you a hardware expert or financial investor?
Concept · Mechanical design
Amoeba
A geared Circular Polarizer Filter (CPL) for the iPhone 17/18 Pro Max camera system.
37 × 38 mm · 4.3 mm thick · 3 polarizers · 1 leverConcept stage; nothing built yet. Not affiliated with or endorsed by Apple. iPhone is a trademark of Apple Inc.
Listen and zero in on that annoying cricket in your house.
import Foundation
/// A 3D LUT. `data` holds RGB triplets with red varying fastest, then green, then blue (the .cube order).
public struct LUT3D: Equatable, Sendable {
public var size: Int
public var data: [Float]
public var domainMin = Vec3(0, 0, 0)
public var domainMax = Vec3(1, 1, 1)
public var title: String = ""
public init(size: Int, data: [Float]) {
precondition(data.count == size * size * size * 3)
self.size = size; self.data = data
}
public static func identity(size: Int) -> LUT3D { bake(size: size) { $0 } }
/// Evaluate `f` at every grid point.
public static func bake(size n: Int, _ f: (Vec3) -> Vec3) -> LUT3D {
var d = [Float](repeating: 0, count: n * n * n * 3)
let s = 1.0 / Double(n - 1)
var i = 0
for b in 0..<n { for g in 0..<n { for r in 0..<n {
let o = f(Vec3(Double(r) * s, Double(g) * s, Double(b) * s))
d[i] = Float(o.x); d[i + 1] = Float(o.y); d[i + 2] = Float(o.z); i += 3
} } }
return LUT3D(size: n, data: d)
}
@inline(__always) private func at(_ r: Int, _ g: Int, _ b: Int) -> Vec3 {
let i = ((b * size + g) * size + r) * 3
return Vec3(Double(data[i]), Double(data[i + 1]), Double(data[i + 2]))
}
/// Tetrahedral interpolation (the method Resolve uses).
public func sample(_ v: Vec3) -> Vec3 {
let m = Double(size - 1)
func coord(_ x: Double, _ lo: Double, _ hi: Double) -> (Int, Double) {
let t = min(max((x - lo) / (hi - lo), 0), 1) * m
let i = min(Int(t), size - 2)
return (i, t - Double(i))
}
let (r0, fr) = coord(v.x, domainMin.x, domainMax.x)
let (g0, fg) = coord(v.y, domainMin.y, domainMax.y)
let (b0, fb) = coord(v.z, domainMin.z, domainMax.z)
let c000 = at(r0, g0, b0), c111 = at(r0 + 1, g0 + 1, b0 + 1)
if fr > fg {
if fg > fb {
let c100 = at(r0 + 1, g0, b0), c110 = at(r0 + 1, g0 + 1, b0)
return c000 * (1 - fr) + c100 * (fr - fg) + c110 * (fg - fb) + c111 * fb
Mac · iPhone · Colour
QuickLUT
Build camera LUTs for Apple Log footage, preview the grade live, and export .cube files and graded clips.
import Foundation
import CoreMediaIO
import CoreMedia
import CoreVideo
/// Pushes frames into the Camera Extension's sink stream (the extension republishes them as the "DSLR Connect" camera).
final class SinkSender {
static let deviceUID = "D51C0A11-7B3E-4F52-9C1A-0E5D2B5D3001"
static let sinkName = "DSLR Connect.Sink"
private let lock = NSLock()
private var deviceID: CMIODeviceID = 0
private var streamID: CMIOStreamID = 0
private var queue: CMSimpleQueue?
private var lastAttempt = Date.distantPast
private var failedEnqueues = 0
private var loggedMissing = false
private var loggedFirstSend = false
/// Lets CoreMediaIO deliver device add/remove notifications to this process, so a camera extension that is
/// installed or approved while the app is running shows up without restarting the app.
static func enableDeviceNotifications() {
var a = address(0x726E6C70) // kCMIOHardwarePropertyRunLoop ('rnlp')
var runLoop = CFRunLoopGetMain()
let st = CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &a, 0, nil,
UInt32(MemoryLayout<CFRunLoop?>.size), &runLoop)
if st != noErr { logLine("sink: could not set CoreMediaIO run loop (\(st))") }
}
private var loggedViewerFormat = false
/// How many apps are currently reading the camera, or nil if that can't be determined.
func viewerCount() -> Int? {
lock.lock(); defer { lock.unlock() }
guard queue != nil else { return nil }
var a = Self.address(0x76777273) // 'vwrs'
guard CMIOObjectHasProperty(deviceID, &a) else { return nil }
var size: UInt32 = 0
guard CMIOObjectGetPropertyDataSize(deviceID, &a, 0, nil, &size) == noErr, size >= 4 else { return nil }
var buf = [UInt8](repeating: 0, count: Int(size))
var used: UInt32 = 0
guard CMIOObjectGetPropertyData(deviceID, &a, 0, nil, size, &used, &buf) == noErr else { return nil }
Mac app · Camera extension · Website
DSLR Connect
Turns a DSLR into a webcam for Zoom, FaceTime and OBS, with a native menu-bar app and a virtual camera.
The craft behind it.
Logos, book covers, maps, product shoots and motion — and the tools I make it all with.