from led_panel import LEDPanel
import time
import math
# 30-LED strip on pin 2
# strip = LEDPanel(pin=6, num_leds=32)
# strip[0] = (25, 0, 0)
# strip.show()
#
# 8×8 matrix on pin 2
WIDTH=16
HEIGHT=8
BRIGHTNESS=255
# panel = LEDPanel(pin=6, num_leds=(HEIGHT*WIDTH), width=WIDTH)
panel = LEDPanel(pin=6, num_leds=(HEIGHT*WIDTH), width=WIDTH, panel_width=8, brightness=BRIGHTNESS, layout="progressive")
_rng_state = time.ticks_us() # Seed from current time (varies each run)
def _rng():
"""Xorshift32 PRNG — returns a pseudo-random 32-bit integer."""
global _rng_state
_rng_state ^= (_rng_state << 13) & 0xFFFFFFFF
_rng_state ^= (_rng_state >> 17)
_rng_state ^= (_rng_state << 5) & 0xFFFFFFFF
return _rng_state & 0xFFFFFFFF
def hsv_to_rgb(hue, saturation, value):
"""
Convert HSV color to RGB.
HSV (Hue-Saturation-Value) is much easier to work with for
animations than RGB:
- hue: 0.0-1.0, the color wheel position
- saturation: 0.0 = gray, 1.0 = vivid color
- value: 0.0 = black, 1.0 = full brightness
Returns (red, green, blue) in 0-63 range (dimmed for LED panels —
full 255 on all LEDs would draw too much current).
The algorithm divides the hue circle into 6 sectors of 60 degrees
and linearly interpolates between the primary/secondary colors
in each sector. This is the standard HSV-to-RGB conversion
used in image processing and computer graphics.
"""
if saturation == 0:
# No saturation = grayscale
gray = int(value * 63)
return (gray, gray, gray)
# Determine which 60-degree sector of the color wheel we're in
sector = int(hue * 6) # Sector index (0-5)
fraction = hue * 6 - sector # Fractional position within sector
# Pre-compute the three interpolated components.
# These formulas come from linearly blending between the two
# primary/secondary colors at the edges of each sector.
min_component = int(value * (1 - saturation) * 63)
decreasing = int(value * (1 - fraction * saturation) * 63)
increasing = int(value * (1 - (1 - fraction) * saturation) * 63)
max_component = int(value * 63)
sector %= 6 # Wrap sector index
if sector == 0: return (max_component, increasing, min_component) # Red to Yellow
if sector == 1: return (decreasing, max_component, min_component) # Yellow to Green
if sector == 2: return (min_component, max_component, increasing) # Green to Cyan
if sector == 3: return (min_component, decreasing, max_component) # Cyan to Blue
if sector == 4: return (increasing, min_component, max_component) # Blue to Magenta
return (max_component, min_component, decreasing) # Magenta to Red
# Test pixel cascade order pixel-by-pixel
# for i in range(128):
# panel.clear()
# panel[i] = (0, 20, 0)
# panel.show()
# print(f"index {i}")
# time.sleep(0.2)
#
# panel.clear()
# panel.show()
# while True:
# for c in range(55):
# for y in range(HEIGHT):
# for x in range(WIDTH):
# # panel.clear()
# panel.pixel(x, y, (50+c, 100-c, c))
# panel.show()
# # print(f"x={x}, y={y}")
# # time.sleep(0.0005)
def plasma(duration):
"""
Plasma effect — classic demoscene algorithm from the 1990s.
TECHNIQUE: Additive sine waves
--------------------------------
Plasma works by combining multiple sine waves at different scales
and speeds. At each pixel, we compute:
v = sin(x) + sin(y) + sin(x+y) + sin(distance_from_center)
The result is a smoothly varying value that we map to a color.
Different frequencies and phase speeds create organic, flowing
patterns that never repeat exactly.
This is the same math behind many screensavers, shader effects,
and generative art. The key insight: adding sine waves at different
frequencies creates complex patterns from simple math.
"""
print(" 4 sine waves at different scales are added per pixel:")
print(" v = sin(x) + sin(y) + sin(x+y) + sin(distance)")
print(" The sum becomes a color. Shifting phase over time animates it.")
start_time = time.ticks_ms()
while True if duration < 0 else time.ticks_diff(time.ticks_ms(), start_time) < duration * 1000:
elapsed = time.ticks_diff(time.ticks_ms(), start_time) / 1000
for y in range(HEIGHT):
for x in range(WIDTH):
wave_sum = math.sin(x * 0.5 + elapsed * 2) # Vertical bands
wave_sum += math.sin(y * 0.5 + elapsed * 1.5) # Horizontal bands
wave_sum += math.sin((x + y) * 0.3 + elapsed) # Diagonal bands
wave_sum += math.sin( # Radial ripple
math.sqrt(x * x + y * y) * 0.4
)
# wave_sum ranges from -4 to +4 — normalize to 0.0-1.0 for hue
hue = (wave_sum / 4.0 + 0.5) % 1.0
r, g, b = hsv_to_rgb(hue, 1.0, 0.8)
panel.pixel(x, y, (r, g, b))
panel.show()
time.sleep_ms(1)
plasma(-1)J17