toxi_openframeworks · PixelSort
Pixel Sort (Glitch Art)
Per-row pixel sorting à la Kim Asendorf's `ASDFPixelSort` and Karsten Schmidt's own glitch-art Processing sketches. For every row, find contiguous runs of pixels whose metric (brightness, RGB channel, or hue) lies above (or below) a threshold; each qualifying run is sorted by the chosen channel. The source image is procedurally regenerated each step (gradient + sinusoidal bands + noise wash) so the artwork evolves over time rather than being a one-shot transformation.
Original: https://github.com/kimasendorf/ASDFPixelSort
▶ 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.
Math essence
For each row y, mask = metric(img[y]) >= threshold (or <= for dark mode). Walk the row; when a run of length in [min_run, max_run] is found, np.argsort the run by the selected channel and write back. A fraction `pass_prob` of rows are skipped entirely to break up the pattern. Metric is BT.601 luminance (default), or any of {R, G, B}, or hue computed from RGB max-min.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 256 | 96–800 | Square canvas side length in pixels. |
mode | str | bright | — | Which pixels qualify for sorting: bright (above threshold), dark (below), white (very high), black (very low). |
sort_channel | str | value | — | Channel to sort each run by. |
threshold | float | 0.55 | 0.05–0.95 | Brightness/darkness threshold for inclusion. |
min_run | int | 6 | 2–64 | Minimum run length to qualify. |
max_run | int | 96 | 16–512 | Maximum run length to qualify. |
pass_prob | float | 0.7 | 0–1 | Probability that any given row is processed. |
frame | int | 0 | 0–9999 | Animation frame counter (changes the source gradient). |
seed | int | null | 0–999999 | RNG seed. |
Reference
First 60 lines of reference.py. Full source:
library_external/toxi_openframeworks/PixelSort/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
def _build_source(dim: int, rng: np.random.Generator, frame: int) -> np.ndarray:
yy, xx = np.mgrid[0:dim, 0:dim].astype(np.float32)
t = frame * 0.02
base_r = 0.5 + 0.5 * np.sin(xx / dim * 6.0 + t * 1.3)
base_g = 0.5 + 0.5 * np.cos(yy / dim * 5.0 - t * 0.7 + 1.1)
base_b = 0.5 + 0.5 * np.sin((xx + yy) / dim * 4.0 + t * 0.5 + 2.2)
bands = 0.7 + 0.3 * np.sin(yy / dim * 18.0 + t * 0.4)
perturb = rng.normal(0, 0.05, size=(dim, dim)).astype(np.float32)
img = np.stack(
[
np.clip(base_r * bands + perturb * 0.5, 0, 1),
np.clip(base_g * bands + perturb * 0.5, 0, 1),
np.clip(base_b * bands + perturb * 0.5, 0, 1),
],
axis=-1,
)
return (img * 255).astype(np.uint8)
class PixelSort:
"""Per-row pixel sorting (Kim Asendorf / Karsten Schmidt glitch art).
A source image (here a procedurally-generated gradient + sinusoidal
bands + noise wash) is processed row by row. In every row we scan
for connected runs of pixels whose ``metric`` value lies above
``threshold`` (the *bright* mode); each run is sorted by the
selected channel. The result is the streak-y, databend look that
Kim Asendorf popularised in `ASDFPixelSort` (2010) and that
Karsten Schmidt has used in his own glitch-art Processing sketches.
Each ``step()`` regenerates the source with a new perturbation
seed, producing an evolving glitch artwork rather than a single
frame.
"""
family: ClassVar[str] = "toxi_openframeworks"
sketch: ClassVar[str] = "pixelsort"
def __init__(
self,
dim: int = 256,
mode: str = "bright",
sort_channel: str = "value",
threshold: float = 0.55,
min_run: int = 6,
max_run: int = 96,
pass_prob: float = 0.7,
frame: int = 0,
seed: int | None = None,
) -> None:
self.dim = dim
self.mode = mode
self.sort_channel = sort_channel Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/toxi_openframeworks/PixelSort/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from toxi · openFrameworks
Generative Type Letterforms
toxi_openframeworks · GenerativeTypeLetterforms
Letterforms assembled from a 9×9 stroke grid alphabet (A–Z subset), then modulated by a parametric function (bar widths and stroke positions follow `a * sin(b*x + c)` style curves) and optionally recursively subdivided into smaller marks.
Gray-Scott Reaction-Diffusion
toxi_openframeworks · ReactionDiffusionGS
The canonical two-species Gray-Scott reaction-diffusion model on a periodic square grid: dU/dt = Du*lap(U) - U*V^2 + F*(1-U), dV/dt = Dv*lap(V) + U*V^2 - (F+k)*V.
Median-Cut Color Quantizer
toxi_openframeworks · ColorQuantizer
Heckbert's 1982 median-cut color quantization applied to an evolving procedural gradient + low-resolution noise wash.
Sound-Reactive Particle Field (Synthetic Audio)
toxi_openframeworks · SoundReactiveField
Particles advected by a force field whose horizontal component is driven by a synthetic audio signal: a chain of N sinusoids at different frequencies/amplitudes (the 'audio'), with the audio's instantaneous value plus its time derivative ('beat onset') driving the field.
Verlet Particle-Spring Network
toxi_openframeworks · ParticleSpring2D
A Verlet-integrated particle network connected to its k-nearest neighbours by Hooke's-law springs.
Voronoi Tessellation with Lloyd Relaxation
toxi_openframeworks · VoronoiRelaxation
Lloyd's algorithm applied to a jittered point cloud: each seed is replaced with the centroid of its Voronoi cell, repeatedly, producing ever-more-uniform polygonal cells.