mokume

mokume

Creative coding for Swift + Metal.

Swift と Metal で、絵と動きを書く。1 ファイルから始めて、保存するたびに絵が差し替わる。

背景はブラウザのシェーダで描いた木目の飾り。mokume が描いた実際の絵は、下に並べてある。

// 01 · live reload

保存すると、その場で差し替わる

mokume watch で走らせたまま書く。保存するたびに作り直して、ウィンドウの中身だけを入れ替える。 ウィンドウを閉じて開き直す手間が無いので、数字を 1 つ変えて見る、を何十回でも繰り返せる。

mokume watch           # 保存したら作り直して差し替える
mokume mcp             # 走っているスケッチを、エージェントから撮る・読む・操作する
左にスケッチのコード、右に実行中の絵。fill の行を書き換えて保存すると橙の円が水色に、circle の大きさを書き換えて保存すると円が大きくなる

// 02 · 3D & light

立体と光を、同じ書き味で

視点を置き、光を置き、塗って形を呼ぶ。2D と同じ fill と push / pop のまま、 影も輪郭線も付く。

let angle = time * 0.5
camera(
    center.x + sin(angle) * distance, center.y - 210, cos(angle) * distance,
    center.x, center.y + 110, 0,
    0, 1, 0)
ambientLight(111, 115, 129)
directionalLight(.linear(red: 0.9, green: 0.86, blue: 0.78), -0.45, 0.85, -0.35)
shadows(true)

fill(242, 115, 76)
box(90)

Sketches/SolidsAndLight.swift から抜粋 (一部を短縮)

床の上に円柱・円錐・輪・箱・球が並び、視点が回るにつれて光の当たる面と影の向きが変わる

// 03 · particles & forces

粒を撒いて、力で曲げる

渦を描く命令はどこにも無い。でたらめな向きに出した粒に 4 つの力を足すと、 2 本の腕が巻いて出てくる。どれか 1 つを外すと、絵はまったく別のものになる。

emit(
    sparks, from: .point(eye.x + cos(angle) * ring, eye.y + sin(angle) * ring),
    toward: .plane(0...(2 * Float.pi)), rate: 800, speed: 30...90, life: 2.4...4,
    size: 1.5...4, color: color(255, 173, 71))

force(
    sparks,
    .attract(eye.x, eye.y, strength: 420),
    .swirl(eye.x, eye.y, strength: 350),
    .drag(1.2),
    .gravity(0, 60))
particles(sparks)

Sketches/SparksAndForces.swift から抜粋

円の上を向かい合って回る 2 つの噴き口から橙の粒が出て、2 本の腕のような渦を描く

// 04 · GPU compute

GPU で場を計算し、そのまま塗る

計算のシェーダで場を毎フレーム書き換え、同じ場を塗りのシェーダで読む。 CPU へ読み戻すのは、要るときだけでいい。

stir.set("time", time)
compute(stir, over: columns, by: rows, writes: [field])

numbers(field)
shader(paint)
rect(0, 0, width, height)
resetShader()

Sketches/FieldAndFlow.swift から抜粋

光を受けた起伏のように青緑の谷と琥珀の尾根が流れて形を変え、その上を等高線が走る。頂に照準が置かれ、下の帯がその行の断面を描く

// 05 · density & effects

描く細かさを落として、効果を重ねる

描く細かさを半分と指定して拡大し、描き終えた絵ににじみ・色ずれ・周辺減光を重ねる。 指定するのは細かさだけで、コードに拡大は 1 行も出てこない。

var settings: SketchSettings {
    SketchSettings(
        width: 960, height: 540, title: "glow and detail", pixelDensity: 0.5)
}

effects([
    .bloom(amount: 0.45 + 0.35 * (0.5 + 0.5 * sin(time * 1.1)), threshold: 0.4, radius: 14),
    .fringe(amount: 0.55),
    .vignette(amount: 0.6),
])

Sketches/GlowAndDetail.swift から抜粋

放射状の線が回りながら、にじみの強さが周期的に強くなったり弱くなったりする

// sketchbook

スケッチ帳

上の絵はすべて参照スケッチが描いたもの。どれも 1 ファイルで、そのまま走らせられる。

// getting started

はじめる

  1. 入れる

    macOS 26 (Tahoe) 以上・Apple Silicon 専用。

    brew install mokume-metal/tap/mokume

    Homebrew を使わない入れ方は 利用ガイド、 このリポジトリから道具を作る手順は CONTRIBUTING にある。

  2. 最初の 1 本

    mokume new my-sketch
    cd my-sketch
    mokume run             # 作って走らせる
    mokume watch           # 保存したら作り直して差し替える
  3. 書く

    できるのは、そのまま動く 1 ファイルのスケッチ:

    import mokume
    
    @main
    final class MySketch: Sketch {
        var settings: SketchSettings {
            SketchSettings(width: 960, height: 540, title: "my-sketch")
        }
    
        func draw() {
            background(15, 18, 23)
            fill(242, 115, 51)
            let angle = time * 0.8
            circle(width / 2 + cos(angle) * 160, height / 2 + sin(angle) * 120, 80)
        }
    }

// next

もっと読む