// Demoscene plasma: four sine waves summed and looked up in a palette, // drawn into 18 frames while two of the waves drift and the palette // cycles. The last frame leads back into the first, so it loops. Each // cell is 2x2 pixels: chunkier, and a quarter of the work per frame. let pal = ["#0A0A1E", "#14193F", "#1B2A6E", "#2E2A57", "#5A1236", "#A51D45", "#E02948", "#FF5B3E", "#FFD23C", "#FFF3B0"]; let cell = 2; let cols = ps.doc_width() / cell; let rows = ps.doc_height() / cell; let cx = cols.to_float() / 2.0; let cy = rows.to_float() / 2.0; let frames = 18; // The two waves that stay put, and each cell's distance from the // center, worked out once. let still = []; let ring = []; for y in 0..rows { for x in 0..cols { let fx = x.to_float(); let fy = y.to_float(); let dx = fx - cx; let dy = fy - cy; still.push(sin(fx / 5.5) + sin(fy / 3.5)); ring.push(sqrt(dx * dx + dy * dy) / 3.0); } } // The palette folded into 18 steps: dark, hot, dark. let fold = []; for k in 0..18 { let p = if k < 10 { k } else { 18 - k }; fold.push(pal[p.min(9)]); } ps_timeline_set_fps(12); for f in 0..frames { if f > 0 { ps_timeline_add_frame(); } let t = f.to_float() * 2.0 * PI() / frames.to_float(); // The diagonal wave depends only on x + y: one sine per diagonal. let diag = []; for d in 0..cols + rows { diag.push(sin(d.to_float() / 7.5 + t)); } for y in 0..rows { for x in 0..cols { let i = y * cols + x; let v = still[i] + diag[x + y] + sin(ring[i] - t); // v runs -4..4. Shift one palette step per frame to cycle. let k = (((v + 4.0) * 2.25).floor().to_int() + f) % 18; ps.pixel_fill(x * cell, y * cell, x * cell + 1, y * cell + 1, fold[k]); } } } print(`${frames} frames of ${cols * rows} cells from four sine waves`);