I’m certain that a million people have tried this before me, but…
Wow, the small rectangular bits of broken tempered glass make a fantastic diffuser! Here’s the prototype for a larger sculpture I’m working on – it’s a strip of 70 APA102 LEDs mounted on one side of a transparent tube, which is filled with glass bits. There’s a lot more subtle color variation and reflected sparkle IRL, but the video shows the overall look pretty well.
It’s running a bubble column pattern that I hacked together for the occasion because it just looked cool with all the internal reflections – perlin noise to color the background and time bubble releases, and a simple particle system for the bubbles. Pixelblaze makes this so easy lately!
Super cool! I got to see it better and understand how transfixing it is when made sure YouTube was serving me the HD version in a full-screen window, and then I could see the fine details in this glass!
I’m glad I have a 4K portrait monitor for these videos!
I think it could use bubbles with different speeds, dim “small” bubble moving fast and bright “big” bubbles moving more slowly. Oh and when the small bubbles reach a big bubble they get trapped behind it then join it and make it bigger.
Great ideas, @sorceror! I’ll post code when I’m done - hoping it will look good on any well diffused vertical display.
tl;dr - discussion for anyone interested in how the sausage is made:
Right now, groups of bubbles are injected at the bottom at slightly varied “slow” speeds, and accelerate towards a terminal velocity as they move upward. You can see that they do separate slightly as they move up, but this could definitely use more variation. I kept the range of speeds narrow, and didn’t allow much slowing down because…
this first cut uses a heating/cooling model, like “Sparks” or “Fireflies” to draw the bubbles. It runs fast, and is easy to code, but it has a problem in that movement in the “up/forward” direction happens in discrete 1-pixel jumps, so if slowed, it becomes nastily jumpy.
For v2, I’ll try distance-based rendering, which gives good anti-aliasing in all directions and will allow a wider range of bubble speeds. I’ll also borrow code from my old “Metaballs of Fire” pattern to handle bubble merging. That ought to fix it right up!
And oh yes, a warp core would be definitely be awesome!!
Here’s the current code. I’ve not posted it to the library, because it’s fairly specific to a certain type of display – a 1D setup with maybe a couple hundred pixels per vertical and a really good diffuser. Doesn’t necessarily look great on bare 5 meter strips.
Anyway, this version launches bubbles at a tunable rate, with lots of random speed variance, merging bubbles, and an interesting “fizz” effect (which can be disabled) when bubbles collide.
Bubble Column
// Bubble Column
// Rising bubbles in a slowly swirling fluid for
// well diffused, vertically oriented 1D displays
//
// MIT License
// Take this code and use it to make cool things!
//
// 12/18/23 ZRanger1
var numBubbles = 9
var bubbleSize = 3
var valveOpen = 0.6
var fluidH = 0.64
var fluidB = 0.08
var startVelocity = 10
var velocityRange = 1.5 * startVelocity;
var acceleration = 0.02
var velocity = array(numBubbles)
var position = array(numBubbles)
var pixels = array(pixelCount)
var timebase = 0
// start bubbles off the display so they'll
// get injected over time by the normal mechanism.
for (i = 0; i < numBubbles; i++) {
position[i] = pixelCount + 10
}
// UI
// base hue of "fluid" in column.
export function hsvPickerFluidHue(h,s,v) {
fluidH = h
}
// higher == new bubbles more often
export function sliderBubbleValve(v) {
valveOpen = 0.8 * (1 - v)
}
function randomSpeed() {
return startVelocity + random(velocityRange) - velocityRange / 2
}
export function beforeRender(delta) {
delta /= 1000;
timebase = (timebase + delta) % 3600;
valve = 0.5 + 0.5 * perlin(timebase / 2,timebase,21.76,PI2);
// check each bubble's contribution to each pixel
for (pixel = 0; pixel < pixelCount; pixel++) {
pixels[pixel] = 0;
for (bubble = 0; bubble < numBubbles; bubble++) {
// the line below is (a) a minor optimization, and
// (b) adds an "effervescent" effect when bubbles
// merge. Comment it out if you want smoother bubbling.
if (pixels[pixel] > fluidB) break;
dist = 1-min(1,abs(pixel - position[bubble])/bubbleSize)
pixels[pixel] += dist * dist * dist * dist
}
}
// move the bubbles
for (bubble = 0; bubble < numBubbles; bubble++) {
position[bubble] += velocity[bubble] * delta;
velocity[bubble] += acceleration;
// let the bubble run slightly off the end of the strip so
// decay looks right at top of column.
// When the bubble is no longer visible, set up to
// reinject it at the bottom if the "valve" is open.
//
if (position[bubble] > pixelCount) {
if (valve >= valveOpen) {
position[bubble] = 0;
velocity[bubble] = randomSpeed();
}
}
}
}
export function render(index) {
a = 0.165*perlin(11*(index/pixelCount - timebase / 10),66.6,33.3,PI2)
v = pixels[index]
if (v <= fluidB) {
hsv(fluidH + a, 1, fluidB)
}
else {
hsv(fluidH + a, 0.45 , v)
}
}
This is awesome! I really would love to see that in the pattern library, just add a comment about it (and a link to the video would explain everything!) in the comment preamble and I’m sure it would benefit some folks!
Trying to upload it now. It gives me a “Sorry, I can’t upload that pattern file” box.
I just successfully uploaded another pattern. Am I just trying to upload things too often, or did I break the file somehow? (It seems ok – transfers between Pixelblazes anyway.)