### Onions have layers 🐸

```Canvas.setpenopacity(.85);

const grid = 6; // min=3, max=20, step=1
const linePrecision = 20; // min=2, max=40, step=1
const totalPerOnion = 12; // min=3, max=60, step=1
const thickness = 26; // min=3, max=60, step=1
const shakyness = 1; // min=0, max=25, step=0.1
const posOffset = 15; // min=0, max=25, step=0.1

let gridSize = 195;
const size = gridSize / grid;
const turtle = new Turtle();
const shakeRandom = new Random(grid + shakyness + posOffset);

const getPos = (val, rnd) => Math.max(-gridSize/2,Math.min(gridSize/2, (-gridSize/2 + val * size + (rnd-.5) * posOffset)));

// The walk function will be called until it returns false.
function walk(i) {
const gridX = i % grid;
const gridY = (i / grid)|0;

const x1 = getPos(gridX, hash2([gridX, gridY]));
const y1 = getPos(gridY, hash2([gridX, gridY]));

const nextGridX = gridX + 1;
const nextGridY = gridY + 1;

const x2 = getPos(gridX, hash2([gridX, nextGridY]));
const y2 = getPos(nextGridY, hash2([gridX, nextGridY]));

const x3 = getPos(nextGridX, hash2([nextGridX, gridY]));
const y3 = getPos(gridY, hash2([nextGridX, gridY]));

const t = (gridX / grid);

if (gridX > 0 && gridY < grid) onion(x1,y1, x2,y2, shake(thickness, thickness/2), shake(totalPerOnion, 4)|0);
if (gridY > 0 && gridX < grid) onion(x1,y1, x3,y3, shake(thickness, thickness/2), shake(totalPerOnion, 4)|0);

return i + 1 < grid * grid;
}

function onion(x1,y1, x2,y2, thickness, total) {
const angle = Math.atan2(y2-y1, x2-x1) + Math.PI/2;
for(let i=0; i<=total; i++) {
const t = (-0.5 + i / total) * 2;
const cx = lerp(x1, x2, 0.5) + Math.cos(angle) * (t * thickness);
const cy = lerp(y1, y2, 0.5) + Math.sin(angle) * (t * thickness);
curve(x1,y1, cx,cy, x2,y2);
}
}

const lerp = (from,to, t) => from + (to - from) * t;
const smooth = (from,to, t) => {
t = (-2.0 * t * t * t + 3.0 * t * t);
return (to * t + from * (1.0 - t));
}
const quad = (from,control,to, t) => {
const q = 1 - t;
return (q * q) * from + 2 * q * t * control + (t * t) * to;
}

function shake(v,amount) {
return v + -(amount * .5) + shakeRandom.get() * amount;
}

function curve(x1,y1, cx,cy, x2,y2) {
turtle.penup();
for(let i=0; i<=linePrecision; i++) {
const t = i / linePrecision;
const x = quad(x1, shake(cx, shakyness), x2, t);
const y = quad(y1, shake(cy, shakyness), y2, t);
if (i != 0) turtle.pendown();
turtle.goto(x,y);
}
}

// seeded random
function Random(seed) { this.state = (seed * 777) % 2147483647 }
Random.prototype.nextInt = function() { return this.state = (1103515245.0 * this.state + 12543) % 2147483647; }
Random.prototype.get = function() { return this.nextInt() / 2147483647; }

function fract(a) {return a-Math.floor(a);}
function hash(n) { return fract(Math.sin(n) * 1.0e4); }
function hash2(p) { return fract(1.0e4 * Math.sin(17.0 * p[0] + p[1] * 0.1) * (0.1 + Math.abs(Math.sin(p[1] * 13.0 + p[0])))); }

```