# Reaction-diffusion

> Spots and stripes from two chemicals: Turing's idea in Pearson's Gray-Scott model. Change two rates and watch a pattern grow.

Page: https://sciencehub.sh/sciencehub-team/reaction-diffusion

- Project: [sciencehub-team/reaction-diffusion](https://sciencehub.sh/sciencehub-team/reaction-diffusion.md)
- Authors: ScienceHub
- Subjects: developmental-biology, statistical-and-nonlinear-physics
- Topics: turing-patterns, reaction-diffusion, pattern-formation
- 0 stars, 0 forks
- DOI: https://doi.org/10.83371/sh.2610.e6d05
- Source: https://sciencehub.sh/sciencehub-team/reaction-diffusion/source
- Updated 13 Oct 2026

## Notebook

Text the notebook's authors wrote, its cells as Python, and the outputs it produced when it last ran in a browser and was saved.

The script header: the dependencies the notebook declares (PEP 723) and the app it defines.

```python
# /// script
# requires-python = ">=3.12"
# dependencies = ["marimo>=0.25.1", "numpy", "matplotlib"]
# ///

import marimo

__generated_with = "0.25.1"
app = marimo.App(width="medium")
```

### Spots and stripes from two chemicals

In 1952 Alan Turing proposed that two substances which react and spread
at different rates can turn a uniform tissue into a pattern: the spots
of a leopard, the stripes of a zebrafish. Forty years later John
Pearson showed how rich such patterns can be with a two-line model, the
Gray-Scott reaction:

$$\frac{\partial u}{\partial t} = D_u \nabla^2 u - u v^2 + F(1 - u), \qquad
\frac{\partial v}{\partial t} = D_v \nabla^2 v + u v^2 - (F + k)\, v$$

$u$ is fed in at rate $F$, $v$ consumes it to make more of itself and is
removed at rate $F + k$, and $u$ spreads twice as fast as $v$. Small
changes in $F$ and $k$ give spots that divide, mazes, or coral.

```python
import marimo as mo
import numpy as np
import matplotlib.pyplot as plt
```

```python
PRESETS = {
    "Dividing spots": (0.0367, 0.0649),
    "Spots": (0.035, 0.065),
    "Coral": (0.0545, 0.062),
    "Maze": (0.029, 0.057),
    "Worms": (0.078, 0.061),
}
preset = mo.ui.dropdown(list(PRESETS), value="Dividing spots", label="Pattern")
preset
```

```python
feed = mo.ui.slider(0.01, 0.09, step=0.0005, value=PRESETS[preset.value][0], label="Feed rate F")
kill = mo.ui.slider(0.045, 0.07, step=0.0005, value=PRESETS[preset.value][1], label="Removal rate k")
steps = mo.ui.slider(500, 8000, step=500, value=5000, label="Time steps")
mo.vstack([feed, kill, steps])
```

```python
def laplacian(X):
    return np.roll(X, 1, -2) + np.roll(X, -1, -2) + np.roll(X, 1, -1) + np.roll(X, -1, -1) - 4 * X

def gray_scott(F, k, n=128, steps=5000, Du=0.16, Dv=0.08, seed=1):
    """Explicit Euler, five-point Laplacian, periodic boundaries, unit grid and time step.

    F and k may be arrays: each pair then runs as its own dish, all at once.
    """
    F = np.asarray(F, dtype=float)[..., None, None]
    k = np.asarray(k, dtype=float)[..., None, None]
    shape = np.broadcast_shapes(F.shape, k.shape)[:-2] + (n, n)
    rng = np.random.default_rng(seed)
    U = np.ones(shape)
    V = np.zeros(shape)
    for _ in range(10):
        x, y = rng.integers(0, n - 8, 2)
        U[..., x : x + 8, y : y + 8] = 0.5
        V[..., x : x + 8, y : y + 8] = 0.25
    U += 0.02 * rng.random((n, n))
    V += 0.02 * rng.random((n, n))
    for _ in range(steps):
        Lu, Lv = laplacian(U), laplacian(V)
        uvv = U * V * V
        U += Du * Lu - uvv + F * (1 - U)
        V += Dv * Lv + uvv - (F + k) * V
    return U, V
```

```python
with mo.status.spinner(title="Reacting and diffusing"):
    _, V = gray_scott(feed.value, kill.value, steps=steps.value)
fig, ax = plt.subplots(figsize=(5.5, 5.5))
ax.imshow(V, cmap="magma", interpolation="bicubic")
ax.set_axis_off()
ax.set_title(f"v after {steps.value:,} steps, F = {feed.value:.4f}, k = {kill.value:.4f}")
fig.tight_layout()
fig
```

![Figure (image/png)](https://phnwz56jyu4zuwyiwcxaoebads.notebooks.sciencehubusercontent.com/_outputs/eyJuYW1lc3BhY2VJZCI6Im5zLXdicjZya2tkOWZqMG1neGUiLCJwcm9qZWN0SWQiOiJwcm9qLXc1OGU5MDNrNGIyZTExdDIiLCJub3RlYm9va0lkIjoibmItcHQwejducHBoOXgweWU3ZyIsImhhc2giOiJhNDA3ZDAyMmE2ZDllYmM2M2YzYjJkMTNlZDM4MzE5YiJ9.0.7vRHMc5D7Qu8hJNbakbZP7Yv8NDykUD5O4_z61DhARw)

#### Where the patterns live

Pearson mapped which pattern appears for each pair $(F, k)$. Most of the
plane is dull: either $v$ dies out and the dish stays uniform, or it
takes over everywhere. Patterns live in a narrow band between the two,
roughly $0.03 < F < 0.08$ and $0.055 < k < 0.068$. Move a slider a few
thousandths outside it and the dish goes blank; that sensitivity is
part of why biologists still argue about which real patterns are
Turing patterns.

---
**Method and limits.** 128 × 128 periodic grid, explicit Euler with
unit time step and grid spacing, $D_u = 0.16$, $D_v = 0.08$; ten seeded
squares of $v$ plus 2 percent noise start the reaction. Patterns depend
on the seed and the grid size, the regimes do not.
**References.** A. M. Turing, *Phil. Trans. R. Soc. B* 237 (1952) 37;
P. Gray and S. K. Scott, *Chem. Eng. Sci.* 38 (1983) 29;
J. E. Pearson, *Science* 261 (1993) 189.
Fork it to grow a pattern on a sphere or to add a third species.

## Versions

- [ver_01M4SH30DQZNT58264N6NNCSQA](https://sciencehub.sh/sciencehub-team/reaction-diffusion/history/ver_01M4SH30DQZNT58264N6NNCSQA), 13 Oct 2026: Edited (current)
- [ver_01M4SGF8T8JZ3N5GRBTX7KRW4N](https://sciencehub.sh/sciencehub-team/reaction-diffusion/history/ver_01M4SGF8T8JZ3N5GRBTX7KRW4N), 13 Oct 2026: Created

## Cite

ScienceHub (2026). Reaction-diffusion \[Computer software\]. ScienceHub. https://doi.org/10.83371/sh.2610.e6d05

```bibtex
@software{reaction_diffusion_2026,
  author = {ScienceHub},
  title = {Reaction-diffusion},
  year = {2026},
  publisher = {ScienceHub},
  doi = {10.83371/sh.2610.e6d05}
}
```
