Ollin

ParticleLenia

no force law at all. Instead there is an energy field, and the particles walk downhill on it. A ring-shaped kernel measures how crowded each particle is

Run
swift run --package-path Examples Example-Simulation-ParticleLenia
Source
Examples/Simulation/ParticleLenia/Sketch.swift
import Ollin

/// Particle Lenia: no force law at all, just an energy field and particles walking
/// downhill on it. Each one adds up a ring-shaped kernel over its neighbors to see how
/// crowded it is, a growth function scores that crowding, a repulsion term stops anyone
/// standing on anyone, and the particle moves whichever way the total improves.
///
/// From those three lines come membranes, cells that hold their shape, rotors, and
/// things that split in two. The parameters are the whole model: `muK` and `sigmaK` are the
/// ring a particle reaches with, `muG` and `sigmaG` the crowding it prefers, and `cRep`
/// how hard it refuses to be crowded. Color is that crowding measured against what the
/// rule wants, so an interior, a membrane, and a particle out on its own look different.
@main
final class ParticleLenia_Example: Sketch {
    var lenia: ParticleLenia!

    // Live parameters: drag these to feel the model change character. Small moves in muG or
    // sigmaG are the difference between a blob, a ring, and a thing that crawls.
    @Param(2 ... 7, icon: "circle.dashed") var ringRadius = 4.0
    @Param(0.3 ... 1.6, icon: "circle.dotted") var ringWidth = 1.0
    @Param(0.2 ... 1.4, icon: "target") var prefersCrowding = 0.6
    @Param(0.05 ... 0.5, icon: "scope") var fussiness = 0.15
    @Param(0.3 ... 2.5, icon: "arrow.left.and.right") var repulsion = 1.0
    @Param(0.2 ... 4.0, icon: "gauge") var pace = 1.0

    override func setup() {
        lenia = makeParticleLenia(count: 6000, spacing: 8)
    }

    override func draw() {
        lenia.muK = ringRadius
        lenia.sigmaK = ringWidth
        lenia.muG = prefersCrowding
        lenia.sigmaG = fussiness
        lenia.cRep = repulsion
        lenia.speed = pace

        background(Color(white: 0.03))
        blendMode(.add)
        stepParticleLenia(lenia)
        drawParticles(lenia)

        blendMode(.normal)
        drawCaption("Particle Lenia · 6,000 particles walking downhill on one energy field")
    }
}

↑↓ move ↩ open