feat: more matrix stream examples

This commit is contained in:
2025-10-02 21:46:51 +02:00
parent f78dd8b843
commit d1fb5fc96e
6 changed files with 426 additions and 60 deletions

View File

@@ -1,11 +1,17 @@
const dgram = require('dgram');
const {
clamp,
hexToRgb,
samplePalette: samplePaletteFromStops,
toIndex,
} = require('./shared-frame-utils');
const DEFAULT_PORT = 4210;
const DEFAULT_WIDTH = 16;
const DEFAULT_HEIGHT = 16;
const DEFAULT_INTERVAL_MS = 60;
const DEFAULT_BLOB_COUNT = 6;
const SERPENTINE_WIRING = true;
const BASE_BLOB_SPEED = 0.18;
const PHASE_SPEED_MIN = 0.6;
const PHASE_SPEED_MAX = 1.2;
@@ -123,8 +129,8 @@ function generateFrame() {
for (let row = 0; row < height; ++row) {
for (let col = 0; col < width; ++col) {
const energy = calculateEnergyAt(col, row);
const color = samplePalette(energy);
frame[toIndex(col, row)] = color;
const color = samplePaletteFromStops(paletteStops, energy);
frame[toIndex(col, row, width)] = color;
}
}
@@ -151,62 +157,6 @@ function getBlobRadius(blob) {
return blob.minRadius + (blob.maxRadius - blob.minRadius) * oscillation;
}
function toIndex(col, row) {
if (!SERPENTINE_WIRING || row % 2 === 0) {
return row * width + col;
}
return row * width + (width - 1 - col);
}
function samplePalette(value) {
const clampedValue = clamp(value, 0, 1);
for (let index = 0; index < paletteStops.length - 1; ++index) {
const left = paletteStops[index];
const right = paletteStops[index + 1];
if (clampedValue <= right.stop) {
const span = right.stop - left.stop || 1;
const t = clamp((clampedValue - left.stop) / span, 0, 1);
const interpolatedColor = lerpRgb(left.color, right.color, t);
return rgbToHex(interpolatedColor);
}
}
return rgbToHex(paletteStops[paletteStops.length - 1].color);
}
function lerpRgb(lhs, rhs, t) {
return {
r: Math.round(lhs.r + (rhs.r - lhs.r) * t),
g: Math.round(lhs.g + (rhs.g - lhs.g) * t),
b: Math.round(lhs.b + (rhs.b - lhs.b) * t),
};
}
function hexToRgb(hex) {
const normalizedHex = hex.trim().toLowerCase();
const value = normalizedHex.startsWith('#') ? normalizedHex.slice(1) : normalizedHex;
return {
r: parseInt(value.slice(0, 2), 16),
g: parseInt(value.slice(2, 4), 16),
b: parseInt(value.slice(4, 6), 16),
};
}
function rgbToHex(rgb) {
return toHex(rgb.r) + toHex(rgb.g) + toHex(rgb.b);
}
function toHex(value) {
const boundedValue = clamp(Math.round(value), 0, 255);
const hex = boundedValue.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function sendFrame() {
const payload = generateFrame();
const message = Buffer.from(payload, 'utf8');