Original code via
rainbow spiral | 64 second loop#pico8 #postcart (256 chars ~ src in reply)
— zep.p8 (@lexaloffle.bsky.social) 2024-10-25T09:33:19.254Z
::★::cls()for r=4,128,4 do for i=0,7/4,1/4doq=(t()*(1+r/32)/8)%1v0=mid((q-i)*4,1) v1=mid((q-i)*4+2,1)a=i-1/8 x=64+cos(a)*r*0.71 y=64+sin(a)*r*0.71a+=3/8u=cos(a)v=sin(a)if(v1>v0)line(x+u*v0*r,y+v*v0*r,x+u*v1*r,y+v*v1*r,8+(r/4)%8)end end flip() goto ★// gif: www.lexaloffle.com/bbs/?tid=144…
— zep.p8 (@lexaloffle.bsky.social) 2024-10-25T09:34:58.225Z
Reverse-engineered into a html5 widget
https://svonberg.org/wp-content/uploads/2026/09/square.html
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Rotating Geometric Widget</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #111; /* Dark background to match PICO-8 vibe */
overflow: hidden;
}
/* Container to center and scale the canvas */
#canvas-container {
position: relative;
/* Maintaining a square aspect ratio similar to PICO-8 */
width: 90vmin;
height: 90vmin;
max-width: 512px;
max-height: 512px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
canvas {
display: block;
width: 100%;
height: 100%;
background-color: #000; /* PICO-8 cls() default black */
image-rendering: pixelated; /* Give it a retro feel */
}
</style>
</head>
<body>
<div id=”canvas-container”>
<canvas id=”pico-canvas” width=”128″ height=”128″></canvas>
</div>
<script>
// Get canvas context
const canvas = document.getElementById(‘pico-canvas’);
const ctx = canvas.getContext(‘2d’);
// PICO-8 color palette (approximations)
const pico8Palette = [
“#000000”, “#1D2B53”, “#7E2553”, “#008751”,
“#AB5236”, “#5F574F”, “#C2C3C7”, “#FFF1E8”,
“#FF004D”, “#FFA300”, “#FFEC27”, “#00E436”,
“#29ADFF”, “#83769C”, “#FF77A8”, “#FFCCAA”
];
// Helper functions matching PICO-8 behavior
function mid(x, y) {
// PICO-8 mid(x, y, z) usually takes 3 args to clamp.
// The lua snippet uses mid(val, 1). This is a trick in PICO-8 where missing args default to 0.
// So mid((q-i)*4, 1) is equivalent to mid(0, (q-i)*4, 1), which is clamping between 0 and 1.
return Math.max(0, Math.min(x, 1)); // Clamping between 0 and 1, assuming implicit 0 as 3rd arg behavior
}
function t() {
// Returns time in seconds
return performance.now() / 1000;
}
// Main animation loop
function draw() {
// Equivalent to cls()
ctx.fillStyle = pico8Palette[0];
ctx.fillRect(0, 0, 128, 128);
const time = t();
// for r=4,128,4 do
for (let r = 4; r <= 128; r += 4) {
// for i=0,7/4,1/4 do
for (let i = 0; i <= 7 / 4; i += 1 / 4) {
// q=(t()*(1+r/32)/8)%1
const q = (time * (1 + r / 32) / 8) % 1;
// v0=mid((q-i)*4,1)
// The original lua uses a 2-arg mid, which in PICO-8 defaults the 3rd to 0.
// effectively clamping between 0 and 1.
const val0 = (q – i) * 4;
const v0 = Math.max(0, Math.min(val0, 1));
// v1=mid((q-i)*4+2,1)
const val1 = (q – i) * 4 + 2;
const v1 = Math.max(0, Math.min(val1, 1));
// a=i-1/8 x=64+cos(a)*r*0.71 y=64+sin(a)*r*0.71
// Note: PICO-8 sin/cos use turns (0-1), standard JS uses radians (0-2PI)
let a = i – 1 / 8;
const a_rad = a * Math.PI * 2; // Convert turns to radians
// PICO-8 cos() is standard, but sin() is inverted (y goes down)
const x = 64 + Math.cos(a_rad) * r * 0.71;
// Invert sin for standard JS canvas to match PICO-8 visual (PICO-8 sin is negative of math.sin)
const y = 64 + (-Math.sin(a_rad)) * r * 0.71;
// a+=3/8 u=cos(a) v=sin(a)
a += 3 / 8;
const a_rad2 = a * Math.PI * 2;
const u = Math.cos(a_rad2);
const v = -Math.sin(a_rad2); // Invert sin
// if(v1>v0)line(x+u*v0*r,y+v*v0*r,x+u*v1*r,y+v*v1*r,8+(r/4)%8)
if (v1 > v0) {
const startX = x + u * v0 * r;
const startY = y + v * v0 * r;
const endX = x + u * v1 * r;
const endY = y + v * v1 * r;
// Color index: 8+(r/4)%8
const colorIndex = 8 + (Math.floor(r / 4) % 8);
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
// PICO-8 lines are generally 1 pixel wide
ctx.lineWidth = 1;
// Use the palette, fallback to white if out of bounds
ctx.strokeStyle = pico8Palette[colorIndex] || “#FFFFFF”;
ctx.stroke();
}
}
}
// Equivalent to flip() and goto loop
requestAnimationFrame(draw);
}
// Start the loop
window.onload = function() {
draw();
}
</script>
</body>
</html>