OK, well, here goes. It’s not my most aesthetic, and I swear I have 10 different twinklers, but… This was kinda balancing minimal LLOC and minimal variable use. I’ll post the other ones after some beginners have posted theirs (since the others tend to be more readable )
var rand
export function beforeRender(delta) {
v = 1 - time(.01 + rand / 66)
if (v < .05) rand = random(1) // Reliable down to 30 FPS
v *= (1 / (1 - v + rand)) % 1 // Twinkler: https://www.desmos.com/calculator/ra90bl33p8
}
export function render(index) {
hsv(rand << 4, 1, (floor((rand << 8) % pixelCount) == index) * v * v)
}
If I rearrange this for speed over LOC, I'm getting 183 FPS on v3 for 1000 pixels / "no LEDs". Also, comments added to this version as per @Scruffynerf's suggestion.
var rand, pixel, h
function next() {
// A random number between 0 and 1 will have the 16 bits to the right of the
// binary point randomly set to 0 or 1. rand itself will be used directly for timing,
// but we can reuse it to get other random numbers from the lower bits.
rand = random(1)
// Left shift 4. 0b0.10110110 (0.7109375) becomes 0b1011.0110 (11.375).
// `hsv(h,` will drop the integer part of h before the point anyway. so this would
// be like calling `hsv(.375,`
h = rand << 4
// We reuse this trick to left shift 8, giving a maximum rand value of 255.996
// in this case, floor() strips the part to the right of the binary point instead, so we're left
// with a random integer between 0 and pixelCount. Notice that this would need
// to be modified if there were > 256 pixels, and it's not equidistributed unless
// pixelCount is a factor of 256. I'm on a 64-pixel matrix here, so the resulting
// probability mass function is equally distributed
pixel = floor((rand << 8) % pixelCount)
}
export function beforeRender(delta) {
// Minimum time() period of .655 seconds, plus a random 0-100% of ~1 second
// Normally time sawtooths increasing-to-the-right, so if I want to start with fade-outs,
// I just invert the 0->1s to be 1->0s with the preceding `1 -`
// It's worth noting here that we get a second source of randomness somewhat because when
// the argument to time() changes, you don't start at a predictable point on the sawtooth
v = 1 - time(.01 + rand / 66)
// Reset the random stuff driving color, position, and timing when the sawtooth
// almost touches 0. This is one of those tricks I say not to use, but here I
// calculated and noted it's reliable (given the constants in this pattern) as long
// as delta < 1/30s, and even then, it will degrade (by missing the timing reset)
// proportionally to the slowness, and still catch it on some cycles.
if (v < .05) next() // Reliable down to 30 FPS
// Comment this out and you'll see the sawtooths directly.
// Visit the link to see how this transforms 1..0 into twinkling 1..0
// "+ rand" is the chill version. Try "- rand" for more flutter (but be sure to lose sign in render())
v *= (1 / (1 - v + rand)) % 1 // Twinkler: https://www.desmos.com/calculator/ra90bl33p8
// Uncomment this to put delays between blinks, but BE SURE to preserve sign in render (v * v _*v_)
// v -= rand / 4
}
export function render(index) {
// The simple trick here is that brightness is 0 unless `pixel == index` (multiply by false = 0)
hsv(h, 1, (pixel == index) * v * v)
}