Based on Reaction-diffusion worms by Cornus Ammonis.
See also Ammonis worms in the large.
Based on Reaction-diffusion worms by Cornus Ammonis.
See also Ammonis worms in the large.
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Ammonis worms, up close and personal</title> | |
| <style> | |
| body { margin: auto; width: 960px; height: 500px; overflow: hidden; } | |
| </style> | |
| </head> | |
| <body> | |
| <script> | |
| var W = 120, H = 64, N = W*H, M = 8; // Tile dimensions | |
| var doc = document.documentElement, | |
| canvas = document.createElement("canvas"), | |
| cx = canvas.getContext("2d"), | |
| tile = document.createElement("canvas"), | |
| tcx = tile.getContext("2d"); | |
| function init() { | |
| tile.width = W; | |
| tile.height = H; | |
| canvas.width = W * M; | |
| canvas.height = H * M; | |
| document.body.appendChild(canvas); | |
| initReactionDiffusion(); | |
| startReactionDiffusion(); | |
| } | |
| function drawTiles() { | |
| renderTile(); | |
| cx.drawImage(tile, 0, 0, W*M, H*M); | |
| } | |
| var α, β, af, α1, β1, α2, β2, α3, β3, α4, β4, α_temp, β_temp; | |
| function initReactionDiffusion() { | |
| α = []; β = []; | |
| for (var i=0; i<N; i++) { | |
| α[i] = Math.random() * 100 - 50; | |
| β[i] = Math.random() * 100 - 50; | |
| } | |
| α1 = []; α2 = []; α3 = []; α4 = []; | |
| β1 = []; β2 = []; β3 = []; β4 = []; | |
| α_temp = []; β_temp = []; | |
| } | |
| function frame() { | |
| rk4(f, 0.2); | |
| drawTiles(); | |
| af = requestAnimationFrame(frame); | |
| } | |
| function startReactionDiffusion() { | |
| af = requestAnimationFrame(frame); | |
| } | |
| function renderTile() { | |
| var imd = tcx.createImageData(W, H), | |
| d = imd.data; | |
| for (var i=0; i<N; i++) { | |
| var v = ((abClampMax - α[i]) / (abClampMax - abClampMin) * 255)|0; | |
| d[4*i + 0] = v; | |
| d[4*i + 1] = v; | |
| d[4*i + 2] = v; | |
| d[4*i + 3] = 0xFF; | |
| } | |
| tcx.putImageData(imd, 0, 0); | |
| } | |
| var clampMin = -10, | |
| clampMax = 10, | |
| D_a = 0.001, | |
| D_b = 0.004, | |
| abClampMin = -50, | |
| abClampMax = 50; | |
| function f(α_in, β_in, c, δα, δβ, δα_out, δβ_out) { | |
| var α, β; | |
| if (c == 0) { | |
| α = α_in; | |
| β = β_in; | |
| } else { | |
| α = α_temp; | |
| β = β_temp; | |
| for (var i=0; i<N; i++) { | |
| α[i] = α_in[i] + c*δα[i]; | |
| β[i] = β_in[i] + c*δβ[i]; | |
| } | |
| } | |
| for (var y=0; y<H; y++) { | |
| for (var x=0; x<W; x++) { | |
| var i = W*y + x, | |
| px = (x+W-1)%W, sx = (x+1)%W, | |
| py = (y+H-1)%H, sy = (y+1)%H; | |
| var laplacian_a = α[W*py+x] + α[W*y+sx] + α[W*sy+x] + α[W*y+px] - 4*α[i], | |
| laplacian_b = β[W*py+x] + β[W*y+sx] + β[W*sy+x] + β[W*y+px] - 4*β[i]; | |
| δα_out[i] = (laplacian_a % 1.0) + Math.max(clampMin, Math.min(clampMax, | |
| (D_a * (α[i]*α[i] - β[i]*β[i])) + laplacian_b)); | |
| δβ_out[i] = (laplacian_a % 1.0) + Math.max(clampMin, Math.min(clampMax, | |
| (D_b * (2*α[i]*β[i])) - laplacian_a)); | |
| } | |
| } | |
| } | |
| function rk4(f, h) { | |
| f(α, β, 0, null, null, α1, β1); | |
| f(α, β, h/2, α1, β1, α2, β2); | |
| f(α, β, h/2, α2, β2, α3, β3); | |
| f(α, β, h, α3, β3, α4, β4); | |
| for (var i=0; i<N; i++) { | |
| α[i] = Math.max(abClampMin, Math.min(abClampMax, α[i] + (h/6) * (α1[i] + 2*α2[i] + 2*α3[i] + α4[i]) )); | |
| β[i] = Math.max(abClampMin, Math.min(abClampMax, β[i] + (h/6) * (β1[i] + 2*β2[i] + 2*β3[i] + β4[i]) )); | |
| } | |
| } | |
| init(); | |
| </script> | |
| </body> | |
| </html> |