Skip to content
Sample of Simple Harmonic Motion

akten_quayola · SimpleHarmonicMotion

Simple Harmonic Motion

A 100x100 grid of damped harmonic oscillators driven by a periodic central impulse. Each cell integrates a = -k*x - c*v with spring constant k=1 and damping c=0.05; a shared kick at the centre every 60 steps lets complexity ripple outward. The render maps displacement and velocity to a warm-to-cool palette so a high-frequency local interference pattern emerges over the grid — Memo Akten's recurring demonstration that 'complex behaviour emerges from very simple, fully deterministic rules'.

Original: https://www.memo.tv/works/simple-harmonic-motion/

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
akten_quayola
Source
akten × quayola
Evolution
physics
Render
shm_grid_heatmap
Complexity
medium
Agents
10000
Year
2014
Author
Memo Akten (memo.tv)

Math essence

For every cell (i, j): a = -k*x - c*v (and optionally -coupling * laplacian(x)). Integrate via semi-implicit Euler: v += a*dt; x += v*dt with dt = 0.05. Every impulse_period steps, v[centre, centre] += impulse. Render reads x as colour amplitude and v as motion depth.

Parameters

Name Type Default Range Description
grid_n int 100 50–200 Number of SHM cells per side.
dim int 512 256–1024 Output canvas side length.
spring_k float 1 0.5–4 Hookean spring constant.
damping_c float 0.05 0–0.4 Velocity damping coefficient.
impulse float 0.6 0.1–2 Magnitude of the periodic central kick.
impulse_period int 60 10–240 Frames between centre kicks.
coupling float 0 0–0.4 Discrete Laplacian coupling strength between neighbours.

Reference

First 60 lines of reference.py. Full source: library_external/akten_quayola/SimpleHarmonicMotion/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


class SimpleHarmonicMotion:
    """Simple Harmonic Motion — Memo Akten, 2011–ongoing.

    A grid of damped harmonic oscillators.  Each cell carries a 1D oscillator
    of state ``(x, v)`` obeying  ``a = -k*x - c*v``  with spring constant
    ``k = 1`` and damping ``c`` in (0, 1).  Every step integrates a fixed
    impulse at the centre of the grid — Akten's recurring metaphor for
    "complex behaviour emerging from very simple, fully deterministic rules".

    The render maps the displacement ``x`` of every oscillator onto a
    perceptually-tuned gradient: zero displacement = warm bone, max
    displacement = deep indigo, min = pale ember.  The grid is bilinearly
    upsampled and lightly tone-mapped so a 100×100 cell field becomes a
    crisp square artwork.

    Akten writes (memo.tv/works/simple-harmonic-motion):  "A number of agents
    are created and assigned a simple behaviour … on their own each agent
    is monotonous … however the interaction of the different agent's
    behaviours create complex, rich, evolving behaviours and compositions".
    """

    family: ClassVar[str] = "akten_quayola"
    sketch: ClassVar[str] = "SimpleHarmonicMotion"

    def __init__(
        self,
        grid_n: int = 100,
        dim: int = 512,
        spring_k: float = 1.0,
        damping_c: float = 0.04,
        impulse: float = 0.6,
        impulse_period: int = 8,
        coupling: float = 0.18,
        seed: int | None = None,
        background: tuple[int, int, int] = (16, 12, 24),
        **_,
    ) -> None:
        self.grid_n = int(grid_n)
        self.dim = int(dim)
        self.spring_k = float(spring_k)
        self.damping_c = float(damping_c)
        self.impulse = float(impulse)
        self.impulse_period = max(1, int(impulse_period))
        self.coupling = float(np.clip(coupling, 0.0, 1.0))
        self.bg_color = np.array(background, dtype=np.float32)
        self.reset(seed=seed)

    def reset(self, seed: int | None = None) -> None:
        self.rng = np.random.default_rng(seed)
        self.x = self.rng.normal(scale=0.4, size=(self.grid_n, self.grid_n)).astype(np.float32)
        self.v = self.rng.normal(scale=0.2, size=(self.grid_n, self.grid_n)).astype(np.float32)
        self.t = 0
        self.phase = self.rng.uniform(0.0, np.pi * 2.0, size=(self.grid_n, self.grid_n)).astype(np.float32)

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/akten_quayola/SimpleHarmonicMotion/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from akten × quayola

Sample of Gesture Driven Field

Gesture Driven Field

akten_quayola · GestureDrivenField

Particles advected through a vector field whose structure is driven by a synthetic gesture signal: a chain of sinusoids at integer-related frequencies.

View →
Sample of Lissajous 3D

Lissajous 3D

akten_quayola · Lissajous3D

A three-dimensional Lissajous figure rendered as a single precision line drawing on a deep field.

View →
Sample of Mechanical Flower Spline

Mechanical Flower Spline

akten_quayola · MechanicalFlowerSpline

A 2D petal-radius spline r(θ) = r0 + A*cos(p*θ + δ) + B*cos(q*θ) swept 360° around a vertical Y axis to form a procedural flower-like 3D surface, then orthographically projected and depth-shaded.

View →
Sample of Pleated Fabric

Pleated Fabric

akten_quayola · PleatedFabric

A textile grid whose vertical coordinate follows z = A * envelope(y) * sin(2π f x + phase(t)), projected obliquely onto a 2D canvas so the folds read as parallel ridges of light.

View →
Sample of Strata Landscape

Strata Landscape

akten_quayola · StrataLandscape

A geological landscape rendered as N horizontal strata stacked vertically, each band displaced by a slowly growing sinusoid and shaded with its own hue.

View →
Sample of Wave Interference

Wave Interference

akten_quayola · WaveInterference

N drifting point sources emit circular sinusoidal waves whose superposition is tone-mapped into a stormy ocean-like field.

View →

See all akten_quayola artworks →