Simulating High-Velocity Plasma Streams with Curl Noise in Python

2026/9/14

Creative coding is at its best when it merges complex mathematical concepts with striking visual aesthetics. In my recent sketch, Kinetic Fluid Turbulence Ribbons, I set out to simulate the violent, chaotic energy of high-velocity plasma streams colliding in a magnetic chamber.
Visually, the piece is intense: glowing neon ribbons of cyan, electric magenta, and bright lime green violently stretch, twist, and tear across a pitch-black void. To achieve this fluid-like, incompressible motion without the heavy computational overhead of a true Navier-Stokes fluid simulation, I turned to curl noise. Paired with Python's NumPy for rapid matrix operations and py5 for high-performance rendering and additive blending, we can create incredibly dense, cinematic particle systems running smoothly in real-time.

Visual & Aesthetic Approach

The aesthetic goal was to capture the sheer power and velocity of plasma. To do this, I needed the particles to leave persistent, glowing trails rather than just appearing as standalone points. By maintaining a history buffer for every particle, we can draw them as continuous, flowing ribbons that fade smoothly into the darkness. Additive blending (py5.blend_mode(py5.ADD)) is critical here. When multiple translucent ribbons overlap in dense, chaotic eddies, their color values accumulate, creating brilliant, blown-out hotspots that perfectly mimic the blinding light of superheated plasma.

Code & Technical Breakdown

The core of this simulation hinges on two techniques: calculating divergence-free curl noise, and efficiently managing the particle history buffers for drawing the ribbons.

1. Generating Incompressible Flow with Curl Noise

Standard Perlin or OpenSimplex noise can look organic, but it lacks the physical characteristics of a fluid. Particles driven directly by noise tend to converge into "sinks" or get stuck. Fluids, however, are largely incompressible—they flow *around* each other in continuous loops and eddies. To achieve this, we can calculate the curl of the noise field using finite differences:
def get_curl(x, y, t):
    eps = 0.1
    # Sample the noise field around the target point
    n1 = py5.os_noise(x, y + eps, t)
    n2 = py5.os_noise(x, y - eps, t)
    n3 = py5.os_noise(x + eps, y, t)
    n4 = py5.os_noise(x - eps, y, t)
    
    # Calculate partial derivatives
    a = (n1 - n2) / (2 * eps)
    b = (n3 - n4) / (2 * eps)
    
    # The curl is the cross derivative
    return np.array([a, -b])
By taking the partial derivatives of the noise field in the X and Y directions, and then swapping and negating them, we generate a vector field that has zero divergence. When particles are pushed by this vector field, they naturally form the swirling, eddying structures characteristic of turbulent fluids.

2. High-Performance Ribbon Buffers with NumPy

To draw continuous ribbons, each of the 800 particles must remember its last 30 positions. Doing this with standard Python lists in a for loop would cripple the frame rate. Instead, NumPy allows us to shift the entire history buffer in a single, lightning-fast operation.
# Shift the entire history buffer back by one frame for all ribbons simultaneously
for i in range(n_ribbons):
    ribbons_x[i, 1:] = ribbons_x[i, :-1]
    ribbons_y[i, 1:] = ribbons_y[i, :-1]
    
    hx, hy = ribbons_x[i, 0], ribbons_y[i, 0]
    
    # Calculate the new head position using the curl noise field
    curl = get_curl(hx * 0.002, hy * 0.002, t)
    hx += curl[0] * 12
    hy += curl[1] * 12
    
    # Update the head of the ribbon
    ribbons_x[i, 0] = hx
    ribbons_y[i, 0] = hy
With the geometry calculated, drawing the ribbons becomes a matter of iterating through the buffer and applying a fading alpha gradient.
py5.begin_shape()
for j in range(tail_length):
    # Fade the tail out smoothly
    alpha = int(py5.remap(j, 0, tail_length, 255, 0))
    py5.stroke(c[0], c[1], c[2], alpha)
    py5.vertex(ribbons_x[i, j], ribbons_y[i, j])
py5.end_shape()
This combination of py5.begin_shape() and mapped alpha values yields the smooth, neon-tube aesthetic that defines the piece.
kinetic fluid turbulence ribbons 2d p1

Conclusion

Curl noise is an incredibly powerful tool in the creative coder's arsenal. By applying a relatively simple calculus concept (partial derivatives) to a standard noise field, we completely transform the visual output from random wandering into complex, physically-grounded fluid dynamics. Paired with additive blending and persistent trails, it allows us to sculpt pure, chaotic light.