Melting winds
Just me learning turtletoy. Using chat to help me explore appyling golden ratio to various designs.
Log in to post a comment.
// Phi Winds v2
// Inspired by "Triotone winds" by zoso95
// https://turtletoy.net/turtle/ac8e4f5a78
//
// Golden-ratio structured generative wind field.
// Designed for pen plotting with visible separation between lines.
Canvas.setpenopacity(0.28);
const PHI = (1 + Math.sqrt(5)) / 2;
const INV_PHI = 1 / PHI;
// ----------------------------------------------------
// Adjustable controls
// ----------------------------------------------------
const pointCount = 34; // min=13 max=89 step=1
const iterations = 377; // min=89 max=987 step=1
const lineSpacing = 3; // min=1 max=12 step=1
const turbulence = 0.022; // min=0.004 max=0.05 step=0.001
const amplitude = 20; // min=4 max=45 step=1
const tension = 0.48; // min=0.15 max=0.85 step=0.01
const phiInfluence = 0.90; // min=0 max=1.5 step=0.05
const splineSegments = 16; // min=4 max=32 step=1
const seedValue = 789; // min=1 max=9999 step=1
// ----------------------------------------------------
// Turtle
// ----------------------------------------------------
const turtle = new Turtle();
turtle.penup();
// ----------------------------------------------------
// Seeded random number generator
// ----------------------------------------------------
let seed = seedValue;
function random() {
const x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
}
// ----------------------------------------------------
// Cardinal spline
// ----------------------------------------------------
function drawSpline(pts, splineTension, segments) {
const res = [];
const p = pts.slice();
// Duplicate edge points for open spline
p.unshift(pts[1]);
p.unshift(pts[0]);
p.push(pts[pts.length - 2]);
p.push(pts[pts.length - 1]);
for (let i = 2; i < p.length - 4; i += 2) {
for (let s = 0; s <= segments; s++) {
const st = s / segments;
const t1x =
(p[i + 2] - p[i - 2]) * splineTension;
const t2x =
(p[i + 4] - p[i]) * splineTension;
const t1y =
(p[i + 3] - p[i - 1]) * splineTension;
const t2y =
(p[i + 5] - p[i + 1]) * splineTension;
const c1 =
2 * st * st * st -
3 * st * st +
1;
const c2 =
-2 * st * st * st +
3 * st * st;
const c3 =
st * st * st -
2 * st * st +
st;
const c4 =
st * st * st -
st * st;
const x =
c1 * p[i] +
c2 * p[i + 2] +
c3 * t1x +
c4 * t2x;
const y =
c1 * p[i + 1] +
c2 * p[i + 3] +
c3 * t1y +
c4 * t2y;
res.push(x, y);
}
}
if (res.length < 2) return;
turtle.penup();
turtle.goto(res[0], res[1]);
turtle.pendown();
for (let i = 2; i < res.length; i += 2) {
turtle.goto(res[i], res[i + 1]);
}
turtle.penup();
}
// ----------------------------------------------------
// Control points
// ----------------------------------------------------
const points = [];
const velocities = [];
for (let i = 0; i < pointCount; i++) {
const normalized =
i / (pointCount - 1);
const x =
-100 +
normalized * 200;
// Random starting position
const baseNoise =
random() - random();
// Golden-ratio spatial modulation
const phiWave =
Math.sin(
normalized *
Math.PI *
PHI
);
const y =
amplitude *
baseNoise *
(
1 +
phiInfluence *
0.30 *
phiWave
);
points.push(x, y);
velocities.push(0, 0);
}
// ----------------------------------------------------
// Simulation
// ----------------------------------------------------
function updateWind(iteration) {
for (let i = 0; i < pointCount; i++) {
const yIndex =
i * 2 + 1;
const normalized =
i / (pointCount - 1);
// Slowly moving phi-based field
const phiWave =
Math.sin(
normalized *
Math.PI *
PHI +
iteration *
INV_PHI *
0.018
);
const noise =
random() - random();
const localTurbulence =
turbulence *
(
1 +
phiInfluence *
0.28 *
phiWave
);
velocities[yIndex] +=
noise *
localTurbulence;
points[yIndex] +=
velocities[yIndex];
}
}
// ----------------------------------------------------
// Main walk loop
// ----------------------------------------------------
function walk(i) {
updateWind(i);
// IMPORTANT:
// Simulation evolves every step,
// but we only DRAW selected states.
//
// This preserves movement while keeping
// visible separation between plotted lines.
if (i % lineSpacing === 0) {
drawSpline(
points,
tension,
splineSegments
);
}
return i < iterations;
}