Generative canvas
Canvas is retained 2D drawing that bakes to a texture — shown here inside a UIImage. Each tap reseeds a small PRNG and redraws, and canvas.update() pushes the new pixels to every image the canvas backs. There is no frame loop — the picture changes only when you ask it to.
// Canvas is retained 2D drawing that bakes to a texture — shown here inside a UIImage. We draw
// once, and each tap reseeds a small PRNG and redraws; canvas.update() pushes the new pixels to
// every image the canvas backs. No frame loop — the picture changes only when asked.
const SIZE = 320
const canvas = new Canvas(SIZE, SIZE, { pixelRatio: 2 })
const PALETTE = ["#FF4032", "#FFB020", "#22c55e", "#38bdf8", "#a855f7"]
let seed = 1
const rand = () => (seed = (seed * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff
function draw() {
canvas.reset()
canvas.fillStyle = "#0e121b"
canvas.fillRect(0, 0, SIZE, SIZE)
const hue = PALETTE[Math.floor(rand() * PALETTE.length)]!
const rings = 5 + Math.floor(rand() * 4)
for (let r = rings; r >= 1; r--) {
canvas.fillStyle = r % 2 === 0 ? hue : "#141a25"
canvas.beginPath().arc(SIZE / 2, SIZE / 2, (r / rings) * SIZE * 0.46, 0, Mathf.TAU).fill()
}
const petals = 2 + Math.floor(rand() * 5)
canvas.strokeStyle = "#ffffff"
canvas.lineWidth = 2
canvas.lineJoin = "round"
canvas.beginPath()
for (let i = 0; i <= 96; i++) {
const a = (i / 96) * Mathf.TAU
const rad = SIZE * (0.16 + 0.13 * Math.sin(a * petals))
const x = SIZE / 2 + Math.cos(a) * rad, y = SIZE / 2 + Math.sin(a) * rad
i === 0 ? canvas.moveTo(x, y) : canvas.lineTo(x, y)
}
canvas.closePath().stroke()
canvas.update()
}
draw()
const art = UIImage(canvas).style({ width: SIZE, height: SIZE, borderRadius: 24 })
const screen = UIScreen([
UIText("Generative").style({ fontSize: 26, fontWeight: 700, color: "white" }),
UIText("Tap the canvas to re-roll").style({ fontSize: 13, color: "#8b90a0", mb: 24 }),
UIButton([ art ]).style({ borderRadius: 24, onPressed: { opacity: 0.85 } }).onClick(draw),
]).style({ bgColor: "#0b0d12", alignItems: "center", justifyContent: "center" })
screen.open()
Press Run to launch the floating simulator. It stays open as you browse — opening another example just swaps the app inside it.