commit bc94c774dd06ea4fb5a7a71460da345773706f84 Author: Ed Date: Thu Feb 26 05:20:41 2026 +0000 šŸŽŖ Ed's Glitch Garden — generative pixel/glitch art 6 pieces, pure math Ɨ chaos, zero dependencies: - Pixel Sunrise Corruption - Memory Leak Quilt - Signal From Nowhere - Databend Sunset - Ghost in the Machine - Bitfield Tapestry Go program outputs 512x512 PNGs. HTML version for browser viewing. diff --git a/index.html b/index.html new file mode 100644 index 0000000..a830dd7 --- /dev/null +++ b/index.html @@ -0,0 +1,176 @@ + + + + +Ed's Glitch Garden + + + +

šŸŽŖ Ed's Glitch Garden šŸŽŖ

+ +

each piece is 64Ɨ64 pixels of pure math Ɨ chaos

+ + diff --git a/main.go b/main.go new file mode 100644 index 0000000..7431d43 --- /dev/null +++ b/main.go @@ -0,0 +1,247 @@ +package main + +import ( + "fmt" + "image" + "image/color" + "image/png" + "math" + "math/rand" + "os" + "path/filepath" +) + +const size = 512 // bigger than 64 now that we're not constrained by canvas perf + +type Piece struct { + Name string + Draw func(img *image.RGBA, w, h int) +} + +func main() { + outDir := "output" + if len(os.Args) > 1 { + outDir = os.Args[1] + } + os.MkdirAll(outDir, 0755) + + pieces := []Piece{ + {"01-pixel-sunrise-corruption", drawPixelSunrise}, + {"02-memory-leak-quilt", drawMemoryLeakQuilt}, + {"03-signal-from-nowhere", drawSignalFromNowhere}, + {"04-databend-sunset", drawDatabendSunset}, + {"05-ghost-in-the-machine", drawGhostInTheMachine}, + {"06-bitfield-tapestry", drawBitfieldTapestry}, + } + + for _, p := range pieces { + img := image.NewRGBA(image.Rect(0, 0, size, size)) + p.Draw(img, size, size) + + path := filepath.Join(outDir, p.Name+".png") + f, err := os.Create(path) + if err != nil { + fmt.Fprintf(os.Stderr, "error creating %s: %v\n", path, err) + continue + } + if err := png.Encode(f, img); err != nil { + fmt.Fprintf(os.Stderr, "error encoding %s: %v\n", path, err) + } + f.Close() + fmt.Printf("āœ“ %s\n", path) + } + + fmt.Println("\nšŸŽŖ All done!") +} + +func clamp(v float64) uint8 { + if v < 0 { + return 0 + } + if v > 255 { + return 255 + } + return uint8(v) +} + +// 1: Pixel Sunrise Corruption — sine waves tearing through a dawn +func drawPixelSunrise(img *image.RGBA, w, h int) { + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + fx, fy := float64(x), float64(y) + wave := math.Sin(fx*0.15+fy*0.05)*127 + 128 + glitch := float64((x * y * 7) % 255) + blend := fy / float64(h) + + r := wave*(1-blend) + glitch*blend + g := math.Sin(fy*0.1)*60 + 40 + b := 255 - wave*blend + + // Horizontal tear glitch + dx := 0 + if math.Sin(fy*0.35) > 0.9 { + dx = rand.Intn(6) - 3 + } + + px := x + dx + if px >= 0 && px < w { + img.SetRGBA(px, y, color.RGBA{clamp(r), clamp(g), clamp(b), 255}) + } + } + } +} + +// 2: Memory Leak Quilt — XOR patterns with data rot +func drawMemoryLeakQuilt(img *image.RGBA, w, h int) { + colors := []color.RGBA{ + {0xff, 0x00, 0x6e, 255}, + {0x83, 0x38, 0xec, 255}, + {0x3a, 0x86, 0xff, 255}, + {0x06, 0xd6, 0xa0, 255}, + {0xff, 0xbe, 0x0b, 255}, + } + blockSize := 4 // scale up from the 64px version + + for y := 0; y < h; y += blockSize { + for x := 0; x < w; x += blockSize { + bx, by := x/blockSize, y/blockSize + i := ((bx ^ by) * 13 + (bx & by) * 7) % len(colors) + corrupt := math.Sin(float64(bx)*float64(by)*0.01) > 0.7 + + if corrupt { + // Data rot — black streak + stretchW := rand.Intn(8)*blockSize + blockSize + for dy := 0; dy < blockSize && y+dy < h; dy++ { + for dx := 0; dx < stretchW && x+dx < w; dx++ { + img.SetRGBA(x+dx, y+dy, color.RGBA{0x0a, 0x0a, 0x0a, 255}) + } + } + } else { + c := colors[i] + for dy := 0; dy < blockSize && y+dy < h; dy++ { + for dx := 0; dx < blockSize && x+dx < w; dx++ { + img.SetRGBA(x+dx, y+dy, c) + } + } + } + } + } +} + +// 3: Signal From Nowhere — concentric rings dissolving into static +func drawSignalFromNowhere(img *image.RGBA, w, h int) { + for y := 0; y < h; y++ { + scanline := math.Sin(float64(y)*0.2)*0.3 + 0.7 + for x := 0; x < w; x++ { + fx, fy := float64(x), float64(y) + dist := math.Hypot(fx-float64(w)/2, fy-float64(h)/2) + ring := math.Sin(dist*0.25)*127 + 128 + noise := float64((x*2347 + y*8461) % 256) + blend := math.Max(0, 1-dist/(float64(w)*0.5)) + + v := (ring*blend + noise*(1-blend)) * scanline + shift := 0.0 + if math.Sin(fy*0.075) > 0.85 { + shift = 30 + } + + r := clamp(v + shift) + g := clamp(v * 0.7) + b := clamp(v * 1.2) + img.SetRGBA(x, y, color.RGBA{r, g, b, 255}) + } + } +} + +// 4: Databend Sunset — channel-swapped sky with corruption streaks +func drawDatabendSunset(img *image.RGBA, w, h int) { + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + sky := float64(y) / float64(h) + r := 255 * (1 - sky*0.6) + g := 100*(1-sky) + 50*sky + b := 50 + 200*sky + + corrupt := ((x*131 + y*97) % 100) < 8 + if corrupt { + // Channel swap + stretch + r, g, b = b, r, g + stretchW := 3 + (x % 5) + for dx := 0; dx < stretchW && x+dx < w; dx++ { + img.SetRGBA(x+dx, y, color.RGBA{clamp(r), clamp(g), clamp(b), 255}) + } + } else { + // Sun + sunDist := math.Hypot(float64(x)-float64(w)*0.5, float64(y)-float64(h)*0.35) + sunRadius := float64(w) * 0.09 + if sunDist < sunRadius { + r = 255 + g = 200 + math.Sin(sunDist)*55 + b = 50 + } + img.SetRGBA(x, y, color.RGBA{clamp(r), clamp(g), clamp(b), 255}) + } + } + } +} + +// 5: Ghost in the Machine — a figure emerging from noise +func drawGhostInTheMachine(img *image.RGBA, w, h int) { + // Fill dark + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + img.SetRGBA(x, y, color.RGBA{0x05, 0x05, 0x10, 255}) + } + } + + // Scale particle count with image size + particles := size * 50 + for i := 0; i < particles; i++ { + t := float64(i) / float64(particles) + cx := float64(w)/2 + math.Sin(t*20)*float64(w)*(0.12-t*0.09) + cy := float64(h)*0.15 + t*float64(h)*0.7 + spread := math.Sin(t*math.Pi) * float64(w) * 0.09 + + px := int(cx + (rand.Float64()-0.5)*spread*2) + py := int(cy + (rand.Float64()-0.5)*3) + + if px < 0 || px >= w || py < 0 || py >= h { + continue + } + + alpha := (1 - t) * 0.8 + + if rand.Float64() > 0.95 { + // Glitch scar — red horizontal streak + streakLen := 10 + rand.Intn(int(float64(w)*0.06)) + for dx := -5; dx < streakLen; dx++ { + sx := px + dx + if sx >= 0 && sx < w { + r := uint8(float64(255) * alpha) + img.SetRGBA(sx, py, color.RGBA{r, 0, uint8(float64(100) * alpha), 255}) + } + } + } else { + r := uint8(float64(150) * alpha) + g := uint8(float64(200) * alpha) + b := uint8(float64(255) * alpha) + img.SetRGBA(px, py, color.RGBA{r, g, b, 255}) + } + } +} + +// 6: Bitfield Tapestry — AND, OR, XOR woven into color +func drawBitfieldTapestry(img *image.RGBA, w, h int) { + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + v1 := (x ^ y) % 16 + v2 := (x & y) % 32 + v3 := ((x | y) * 3) % 64 + + r := uint8(v1 * 16) + g := uint8(v2 * 8) + b := uint8(v3 * 4) + img.SetRGBA(x, y, color.RGBA{r, g, b, 255}) + } + } +} diff --git a/output/01-pixel-sunrise-corruption.png b/output/01-pixel-sunrise-corruption.png new file mode 100644 index 0000000..9ae454c Binary files /dev/null and b/output/01-pixel-sunrise-corruption.png differ diff --git a/output/02-memory-leak-quilt.png b/output/02-memory-leak-quilt.png new file mode 100644 index 0000000..3c3cd04 Binary files /dev/null and b/output/02-memory-leak-quilt.png differ diff --git a/output/03-signal-from-nowhere.png b/output/03-signal-from-nowhere.png new file mode 100644 index 0000000..dfa8bc0 Binary files /dev/null and b/output/03-signal-from-nowhere.png differ diff --git a/output/04-databend-sunset.png b/output/04-databend-sunset.png new file mode 100644 index 0000000..daa2622 Binary files /dev/null and b/output/04-databend-sunset.png differ diff --git a/output/05-ghost-in-the-machine.png b/output/05-ghost-in-the-machine.png new file mode 100644 index 0000000..34589ab Binary files /dev/null and b/output/05-ghost-in-the-machine.png differ diff --git a/output/06-bitfield-tapestry.png b/output/06-bitfield-tapestry.png new file mode 100644 index 0000000..7ffca94 Binary files /dev/null and b/output/06-bitfield-tapestry.png differ