The story of Origin
Their scientific pursuits had long been focused on this particular region, absorbing a considerable amount of their resources. Centuries were invested in decoding patterns that, despite their relentless efforts, remained stubbornly incomprehensible.
Then it happened.
In a tiny sliver of spacetime, a coherent structure emerged. The entire episode unfolded in an instant, but it was crystal clear. A spontaneous creation with an otherworldly precision, unforeseen and inexplicable, had materialized right before their eyes.
And just as quickly it all dissolved, leaving only a faint, vanishing trail.
Attempts at replication proved unsuccessful, the anomaly slipping through their scientific grasp. But this singular event proved that they had glimpsed the origin of some kind, an undeniable reality forever etched into their collective minds.
A photorealistic, otherworldly 3D scene
This generative artwork uses a technique called Monte Carlo path tracing, which is a physically based rendering algorithm that simulates the way light rays bounce around in a scene. It is computationally expensive as it follows multiple light rays per pixel, but it produces well-lit and realistic images.
The shapes in this artwork are created using pure mathematical equations called signed distance functionsThe invaluable articles from Inigo Quilez helped me learn the math of it all. Using various complex algorithms, these shapes are distorted and combined with each other to create new forms.
Due to its computational complexity, it takes a while to render a crisp image. The longer you let it run, the more detailed and less noisy the final output becomes. By default, the renderer optimizes for a 15 FPS rendering speed by choosing an optimal tile size. This keeps your browser responsive, but renders more slowly. To render an image at maximum capacity, you can either disable tile-based rendering altogether, or choose a preset and let it render for a while.
Variations in the artwork
Origin comes in different editions. Each output is unique and has its own characteristics, like different cell forms, materials and lighting setup.
The combined computational space of custom parameters and controlled randomness results in a wide range of possible outputs. Some are zoomed in and full of light particles, some are shot more from a distance, showing the intricate intertwining of organic shapes amidst a flurry of liquid drops.
Compact code, maximum detail
The entirety of this artwork is created by a single, compact piece of code of only 16kBIt’s exactly 16000 bytes and fits in tiny print on a sheet of paper, without using any external libraries or frameworks. It’s completely self-containedAs a general rule, I avoid external dependencies in my works and designed to pack as much detail as possible into as little code as possible. As the final code is stored on the blockchain, every byte counts. This algorithmic artwork is as much a technical challenge as it is an artistic one.
Several tricks have been applied to achieve this, like shortening variables, rewriting functions without any keywords, packing numbers in strings and using a custom minifier to reduce the size of the shader code even further.
One of the tricks that had the most impact in saving bytes was the comma operatorI learned its practical uses from Piter Pasma, who is known for his extreme hand-coded minification. It’s a little known operator in JavaScript, but it allows you to chain expressions together and return the last one. So the last expression below returns 3:
let a
let b
const value = (a = 1, b = 2, a + b)
The comma operator can eliminate otherwise necessary keywords if you’re willing to code in a less conventional approach. Take this normal function:
function getProgram(ctx, vs, fs) {
const pr = ctx.createProgram()
[vs, fs].map(x => ctx.attachShader(pr, x))
ctx.linkProgram(pr)
return pr
}
Together with default variable declarations and further organizing the function in an object, see how this saves a function
, const
and return
keyword. This adds up as your code get more complex, especially after automatic minification.
{
getProgram: (ctx, vs, fs, pr = ctx.createProgram()) => (
[vs, fs].map(x => ctx.attachShader(pr, x)),
ctx.linkProgram(pr),
pr
)
}
Shader programming
The bulk of the code is written in GLSL, a language that is specifically designed for graphics programming. One of the main advantages of using shaders is that they run on the GPU, which is optimized for parallel processing. This allows for the rendering of complex scenes in a feasible time frame.
Shader code is stored as a long string of text. I found that the tiny overhead of adding a RLE-decompressorA basic data compression algorithm that consists of describing a string according to its repetitions. was worth the effort, as it saved a considerable amount of bytes.
Patience
This artwork has been a practice in patience on multiple levels. It took me a while to grasp shader coding, and it took me even longer to understand how path tracing works. But slowly and surely I came to understand the intricacies of the rendering process and the time investment paid off. I learned a lot about the physics of light and the math behind it.
For the viewer of the live versions, it’s just as well a practice in patience. The rendering process can take a while, especially when you want to render a high-quality image, but the final results are worth it.