“Do you see how the LostSideDead pattern of 1, 2, 2, 3, 3, 3—or L s S d D d—can be linked to evolution? The numbers increase; the next in the sequence would be 4, 4, 4, 4.”
Nested loops → direct lookup → CUDA → compute shaders
Follow the idea ↓An architectural progression
The evolution
of a loop.
A simple expanding sequence becomes a model for complexity—then a path from nested CPU loops to massively parallel GPU compute.
Trace the progression↓The pattern
Lost · Side · Dead
Complexity needs
time to stabilize.
Each state N repeats exactly N times. The progression can be read as an evolutionary rhythm: a change, followed by a longer period in which that change becomes established.
Punctuated equilibrium
Rapid bursts of mutation are followed by stasis. As the architecture grows more complex, its stabilization period grows with it.
The cost of complexity
A more capable system takes more energy, time, and population mass to replicate and sustain.
Branching diversity
Higher evolutionary tiers support more variations, specializations, and niches from the same base blueprint.
The CPU loop
Sequential model
One loop evolves.
The other endures.
The outer loop is the mutation: it advances through tiers. The inner loop is the time domain: it sustains each tier for exactly as many generations as its value.
#include <iostream>
void simulateEvolutionaryExpansion(int max_evolution_tier) {
for (int tier = 1; tier <= max_evolution_tier; ++tier) {
// Stabilization grows with the complexity tier
for (int generation = 0; generation < tier; ++generation) {
std::cout << tier << " ";
}
}
std::cout << "\n";
}
int main() {
simulateEvolutionaryExpansion(4);
// Output: 1 2 2 3 3 3 4 4 4 4
return 0;
}
Flatten the loop
Parallel model
Time becomes
space.
Reverse the triangular number formula and every index can discover its tier directly. There is no need to calculate any of the states that came before it.
#include <cmath>
// Resolve any index without generating earlier states
int getEvolutionTier(double index) {
return static_cast<int>(
std::ceil((std::sqrt(8.0 * index + 1.0) - 1.0) / 2.0)
);
}
// Index 7, 8, 9, and 10 all resolve to tier 4.
Each thread owns one absolute position. The same O(1) expression runs across thousands of indices simultaneously, avoiding the uneven work of an inner loop.
__global__ void generateEvolutionPattern(
int* output_buffer, int total_elements
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < total_elements) {
float n = static_cast<float>(idx + 1);
output_buffer[idx] = static_cast<int>(
ceil((sqrt(8.0f * n + 1.0f) - 1.0f) / 2.0f)
);
}
}
Compute shader
From libacidcam to acidcam-gpu
Every pixel becomes
its own thread.
A compute shader is the graphics-native cousin of a CUDA kernel. The CPU stops visiting every pixel. It dispatches a grid; each invocation identifies its own coordinate and works directly in GPU memory.
Gather
Read from anywhere. Write only to the pixel currently being processed.
Scatter
Read from anywhere. Write anywhere—ideal for smears, sorting, routing, and glitch displacement.
#version 460 core
layout(local_size_x = 16, local_size_y = 16) in;
layout(rgba8, binding = 0) uniform readonly image2D inputFrame;
layout(rgba8, binding = 1) uniform writeonly image2D outputFrame;
void main() {
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(inputFrame);
if (pixel.x >= size.x || pixel.y >= size.y) return;
float index = float(pixel.y * size.x + pixel.x + 1);
int tier = int(ceil(
(sqrt(8.0 * index + 1.0) - 1.0) / 2.0
));
vec4 color = imageLoad(inputFrame, pixel);
ivec2 offset = ivec2(tier % 10, (tier / 10) % 10);
ivec2 target = (pixel + offset) % size;
imageStore(outputFrame, target, color);
}
The zero-copy ideal
Decode, manipulate, and present without returning pixel data to a CPU-side image loop.