Mini de Jong Attractor A small implementation of the Peter de Jong Attractor, inspired by Xen and yonatan on Dwitter. For a much (much) faster WebGL variant of a similar attractor, see Clifford Attractor III. A de Jong attractor is a simple iterated function, recording the path of a single particle as it loops around the canvas. The particle starts at (x = 0, y = 0) and then moves to its next position, as described by:
{ const html = DOM.element('div'); html.append(tex`x_{n+1} = sin(a y_n) - cos(b x_n)`); html.append(DOM.element('br')); html.append(tex`y_{n+1} = sin(c x_n) - cos(d y_n)`); html.append(md`You can change the values of \`a = ${a}\`, \`b = ${b}\`, \`c = ${c}\`, and \`d = ${d}\` below.`); return html; }
viewof S = slider({min: 100 * devicePixelRatio, max: width * devicePixelRatio, value: 640 * devicePixelRatio, step: 100})
viewof fill = slider({min: 0.01, max: 1, value: 0.1, description: "Literally, the dot size of each particle position drawn"})
viewof a = slider({min: -pi, max: pi, value: 1})
viewof b = slider({min: -pi, max: pi, value: pi})
viewof c = slider({min: -pi, max: pi, value: 0.33})
viewof d = slider({min: -pi, max: pi, value: 2.8})
viewof canvas = { const canvas = DOM.element('canvas'); const ratio = window.devicePixelRatio; canvas.width = S; canvas.height = S; canvas.style.width = (S / ratio) + 'px'; canvas.style.height = (S / ratio) + 'px'; canvas.value = canvas.getContext('2d') return canvas; }
resetButton("Soap Camel", -3.03, -3.14, 2.97, -3.14)
resetButton("Folded Paper", -0.39, -1.87, 0.21, -2.9)
resetButton("Smoke Ring", 0.66, -1.46, 0.16, -1.77)
Here, draw is the complete implementation. For every frame, we plot an additional 10,000 positions of the particle.
draw = { let x, y, i, t; x = y = t = 0; canvas.clearRect(0, 0, S, S); while(t < 300) { for (i = 1e4; i--;) { canvas.fillRect(x * S/4 + S/2 , y * S/4 + S/2, fill, fill); x = Math.sin(a * y) - Math.cos(b * x); y = Math.sin(c * x) - Math.cos(d * y); } yield ++t; } }
pi = Math.PI
function resetButton(name, aVal, bVal, cVal, dVal) { function set(el, value) { el.input.value = value; el.dispatchEvent(new window.Event("input")); } const button = DOM.element("button"); button.innerText = name; button.onclick = function() { set(viewof a, aVal); set(viewof b, bVal); set(viewof c, cVal); set(viewof d, dVal); } return button; }
import {slider} from "@jashkenas/inputs"