vizcore 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +544 -9
- data/docs/.nojekyll +0 -0
- data/docs/assets/site.css +744 -0
- data/docs/assets/vizcore-demo.gif +0 -0
- data/docs/assets/vizcore-poster.png +0 -0
- data/docs/assets/vj-tunnel.js +159 -0
- data/docs/index.html +224 -0
- data/examples/README.md +59 -0
- data/examples/assets/README.md +19 -0
- data/examples/audio_inspector.rb +34 -0
- data/examples/club_intro_drop.rb +78 -0
- data/examples/kansai_rubykaigi_visual.rb +70 -0
- data/examples/live_coding_minimal.rb +22 -0
- data/examples/midi_controller_show.rb +78 -0
- data/examples/midi_scene_switch.rb +3 -1
- data/examples/parser_visualizer.rb +48 -0
- data/examples/readme_demo.rb +17 -0
- data/examples/rhythm_geometry.rb +34 -0
- data/examples/ruby_crystal_show.rb +35 -0
- data/examples/shader_playground.rb +18 -0
- data/examples/unyo_liquid.rb +59 -0
- data/examples/vj_ambient_chill_room.rb +124 -0
- data/examples/vj_dnb_jungle.rb +170 -0
- data/examples/vj_festival_mainstage.rb +245 -0
- data/examples/vj_festival_mainstage.yml +17 -0
- data/examples/vj_glitch_industrial.rb +164 -0
- data/examples/vj_hiphop_cipher.rb +167 -0
- data/examples/vj_jpop_idol_live.rb +210 -0
- data/examples/vj_synthwave_retro.rb +173 -0
- data/examples/vj_techno_warehouse.rb +195 -0
- data/frontend/index.html +468 -2
- data/frontend/src/audio-inspector.js +40 -0
- data/frontend/src/live-controls.js +131 -0
- data/frontend/src/main.js +792 -16
- data/frontend/src/midi-learn.js +194 -0
- data/frontend/src/performance-monitor.js +183 -0
- data/frontend/src/plugin-runtime.js +130 -0
- data/frontend/src/projector-mode.js +56 -0
- data/frontend/src/renderer/engine.js +148 -3
- data/frontend/src/renderer/layer-manager.js +428 -30
- data/frontend/src/renderer/shader-manager.js +26 -0
- data/frontend/src/runtime-control-preset.js +11 -0
- data/frontend/src/shader-error-overlay.js +29 -0
- data/frontend/src/shader-param-controls.js +93 -0
- data/frontend/src/shaders/builtins.js +380 -2
- data/frontend/src/shaders/post-effects.js +52 -0
- data/frontend/src/visual-regression.js +67 -0
- data/frontend/src/visual-settings-preset.js +103 -0
- data/frontend/src/visuals/geometry.js +268 -0
- data/frontend/src/visuals/image-renderer.js +291 -0
- data/frontend/src/visuals/particle-system.js +56 -10
- data/frontend/src/visuals/spectrogram-renderer.js +226 -0
- data/frontend/src/visuals/text-renderer.js +112 -11
- data/frontend/src/websocket-client.js +12 -1
- data/lib/vizcore/analysis/adaptive_normalizer.rb +70 -0
- data/lib/vizcore/analysis/beat_detector.rb +4 -2
- data/lib/vizcore/analysis/bpm_estimator.rb +8 -0
- data/lib/vizcore/analysis/feature_recorder.rb +159 -0
- data/lib/vizcore/analysis/feature_replay.rb +84 -0
- data/lib/vizcore/analysis/pipeline.rb +235 -11
- data/lib/vizcore/analysis/tap_tempo.rb +74 -0
- data/lib/vizcore/analysis.rb +4 -0
- data/lib/vizcore/audio/dummy_sine_input.rb +1 -1
- data/lib/vizcore/audio/fixture_input.rb +65 -0
- data/lib/vizcore/audio/input_manager.rb +4 -2
- data/lib/vizcore/audio/mic_input.rb +24 -8
- data/lib/vizcore/audio/portaudio_ffi.rb +106 -1
- data/lib/vizcore/audio.rb +1 -0
- data/lib/vizcore/cli/doctor.rb +159 -0
- data/lib/vizcore/cli/dsl_reference.rb +99 -0
- data/lib/vizcore/cli/layer_docs.rb +46 -0
- data/lib/vizcore/cli/scene_diagnostics.rb +23 -0
- data/lib/vizcore/cli/scene_inspector.rb +136 -0
- data/lib/vizcore/cli/scene_validator.rb +245 -0
- data/lib/vizcore/cli/shader_template.rb +68 -0
- data/lib/vizcore/cli/shader_uniform_docs.rb +54 -0
- data/lib/vizcore/cli.rb +689 -18
- data/lib/vizcore/config.rb +103 -2
- data/lib/vizcore/control_preset.rb +68 -0
- data/lib/vizcore/dsl/engine.rb +277 -5
- data/lib/vizcore/dsl/layer_builder.rb +491 -22
- data/lib/vizcore/dsl/layer_group_builder.rb +112 -0
- data/lib/vizcore/dsl/mapping_resolver.rb +132 -3
- data/lib/vizcore/dsl/mapping_transform_builder.rb +71 -0
- data/lib/vizcore/dsl/reaction_builder.rb +44 -0
- data/lib/vizcore/dsl/scene_builder.rb +61 -5
- data/lib/vizcore/dsl/shader_source_resolver.rb +67 -6
- data/lib/vizcore/dsl/style_builder.rb +68 -0
- data/lib/vizcore/dsl/timeline_builder.rb +138 -0
- data/lib/vizcore/dsl/transition_controller.rb +77 -0
- data/lib/vizcore/dsl.rb +5 -1
- data/lib/vizcore/layer_catalog.rb +273 -0
- data/lib/vizcore/project_manifest.rb +152 -0
- data/lib/vizcore/renderer/png_writer.rb +57 -0
- data/lib/vizcore/renderer/render_sequence.rb +153 -0
- data/lib/vizcore/renderer/scene_frame_source.rb +119 -0
- data/lib/vizcore/renderer/scene_serializer.rb +36 -3
- data/lib/vizcore/renderer/snapshot.rb +38 -0
- data/lib/vizcore/renderer/snapshot_renderer.rb +446 -0
- data/lib/vizcore/renderer.rb +5 -0
- data/lib/vizcore/server/frame_broadcaster.rb +91 -5
- data/lib/vizcore/server/gallery_app.rb +155 -0
- data/lib/vizcore/server/gallery_page.rb +100 -0
- data/lib/vizcore/server/gallery_runner.rb +48 -0
- data/lib/vizcore/server/rack_app.rb +203 -4
- data/lib/vizcore/server/runner.rb +370 -22
- data/lib/vizcore/server/scene_dependency_watcher.rb +79 -0
- data/lib/vizcore/server/websocket_handler.rb +60 -10
- data/lib/vizcore/server.rb +4 -0
- data/lib/vizcore/sync/osc_message.rb +103 -0
- data/lib/vizcore/sync/osc_receiver.rb +68 -0
- data/lib/vizcore/sync.rb +4 -0
- data/lib/vizcore/templates/midi_control_scene.rb +3 -1
- data/lib/vizcore/templates/plugin_layer.rb +20 -0
- data/lib/vizcore/templates/plugin_readme.md +23 -0
- data/lib/vizcore/templates/plugin_renderer.js +43 -0
- data/lib/vizcore/templates/plugin_scene.rb +14 -0
- data/lib/vizcore/templates/project_readme.md +7 -23
- data/lib/vizcore/templates/rubykaigi_scene.rb +30 -0
- data/lib/vizcore/version.rb +1 -1
- data/lib/vizcore.rb +27 -0
- data/scripts/browser_capture.mjs +75 -0
- data/sig/vizcore.rbs +362 -0
- metadata +83 -3
- data/docs/GETTING_STARTED.md +0 -105
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const SHADER_ERROR_EVENT = "vizcore:shader-error";
|
|
2
|
+
|
|
3
|
+
export const buildShaderErrorDetail = ({ layer, error, phase }) => {
|
|
4
|
+
const name = String(layer?.name || "unnamed");
|
|
5
|
+
const shader = String(layer?.glsl || layer?.shader || layer?.type || "unknown");
|
|
6
|
+
return {
|
|
7
|
+
name,
|
|
8
|
+
shader,
|
|
9
|
+
phase: String(phase || "shader"),
|
|
10
|
+
message: normalizeErrorMessage(error),
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const formatShaderErrorTitle = (detail) => {
|
|
15
|
+
const name = String(detail?.name || "unnamed");
|
|
16
|
+
const shader = String(detail?.shader || "unknown");
|
|
17
|
+
return `${name} (${shader})`;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const formatShaderErrorMessage = (detail) => {
|
|
21
|
+
const phase = String(detail?.phase || "shader");
|
|
22
|
+
const message = String(detail?.message || "Unknown shader error");
|
|
23
|
+
return `[${phase}] ${message}`;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const normalizeErrorMessage = (error) => {
|
|
27
|
+
const message = String(error?.message || error || "Unknown shader error").trim();
|
|
28
|
+
return message || "Unknown shader error";
|
|
29
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export const shaderParamLayerKey = (layer, index) => `${index}:${String(layer?.name || "layer")}`;
|
|
2
|
+
|
|
3
|
+
export const shaderParamControlEntries = (layers, overrides = {}) => {
|
|
4
|
+
const entries = [];
|
|
5
|
+
const layerList = Array.isArray(layers) ? layers : [];
|
|
6
|
+
|
|
7
|
+
layerList.forEach((layer, layerIndex) => {
|
|
8
|
+
const schema = Array.isArray(layer?.param_schema) ? layer.param_schema : [];
|
|
9
|
+
const layerKey = shaderParamLayerKey(layer, layerIndex);
|
|
10
|
+
const layerName = String(layer?.name || `layer_${layerIndex + 1}`);
|
|
11
|
+
|
|
12
|
+
schema.forEach((entry) => {
|
|
13
|
+
const paramName = normalizeParamName(entry?.name);
|
|
14
|
+
if (!paramName) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const min = finiteNumber(entry?.min, 0);
|
|
19
|
+
const max = Math.max(min, finiteNumber(entry?.max, min + 1));
|
|
20
|
+
const step = positiveNumber(entry?.step, Math.max((max - min) / 100, 0.01));
|
|
21
|
+
const params = layer?.params || {};
|
|
22
|
+
const baseValue = finiteNumber(
|
|
23
|
+
overrides?.[layerKey]?.[paramName],
|
|
24
|
+
finiteNumber(params[paramName], finiteNumber(entry?.default, min))
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
entries.push({
|
|
28
|
+
key: `${layerKey}:${paramName}`,
|
|
29
|
+
layerKey,
|
|
30
|
+
layerName,
|
|
31
|
+
paramName,
|
|
32
|
+
label: `${layerName}.${paramName}`,
|
|
33
|
+
min,
|
|
34
|
+
max,
|
|
35
|
+
step,
|
|
36
|
+
value: clamp(baseValue, min, max),
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return entries;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const applyShaderParamOverrides = (layers, overrides = {}) => {
|
|
45
|
+
const layerList = Array.isArray(layers) ? layers : [];
|
|
46
|
+
|
|
47
|
+
return layerList.map((layer, layerIndex) => {
|
|
48
|
+
const layerKey = shaderParamLayerKey(layer, layerIndex);
|
|
49
|
+
const layerOverrides = overrides?.[layerKey];
|
|
50
|
+
if (!layerOverrides || typeof layerOverrides !== "object") {
|
|
51
|
+
return layer;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
...layer,
|
|
56
|
+
params: {
|
|
57
|
+
...(layer?.params || {}),
|
|
58
|
+
...layerOverrides,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const pruneShaderParamOverrides = (overrides = {}, entries = []) => {
|
|
65
|
+
const next = {};
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const value = overrides?.[entry.layerKey]?.[entry.paramName];
|
|
68
|
+
if (!Number.isFinite(Number(value))) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
next[entry.layerKey] ||= {};
|
|
73
|
+
next[entry.layerKey][entry.paramName] = Number(value);
|
|
74
|
+
}
|
|
75
|
+
return next;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const normalizeParamName = (value) => {
|
|
79
|
+
const name = String(value || "").trim();
|
|
80
|
+
return name || null;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const finiteNumber = (value, fallback) => {
|
|
84
|
+
const numeric = Number(value);
|
|
85
|
+
return Number.isFinite(numeric) ? numeric : fallback;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const positiveNumber = (value, fallback) => {
|
|
89
|
+
const numeric = finiteNumber(value, fallback);
|
|
90
|
+
return numeric > 0 ? numeric : fallback;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
@@ -145,6 +145,72 @@ void main() {
|
|
|
145
145
|
color += vec3(0.8, 0.85, 1.0) * pulse * (0.1 + u_high * 0.4);
|
|
146
146
|
outColor = vec4(color, 1.0);
|
|
147
147
|
}
|
|
148
|
+
`,
|
|
149
|
+
liquid_wobble: `#version 300 es
|
|
150
|
+
precision mediump float;
|
|
151
|
+
|
|
152
|
+
uniform vec2 u_resolution;
|
|
153
|
+
uniform float u_time;
|
|
154
|
+
uniform float u_amplitude;
|
|
155
|
+
uniform float u_bass;
|
|
156
|
+
uniform float u_mid;
|
|
157
|
+
uniform float u_high;
|
|
158
|
+
uniform float u_beat;
|
|
159
|
+
uniform float u_beat_pulse;
|
|
160
|
+
uniform float u_bpm;
|
|
161
|
+
uniform float u_fft[32];
|
|
162
|
+
uniform float u_fft_size;
|
|
163
|
+
uniform float u_param_wobble;
|
|
164
|
+
uniform float u_param_warp;
|
|
165
|
+
uniform float u_param_distortion;
|
|
166
|
+
uniform float u_visual_gain;
|
|
167
|
+
uniform float u_wobble_amount;
|
|
168
|
+
|
|
169
|
+
out vec4 outColor;
|
|
170
|
+
|
|
171
|
+
float blob(vec2 p, float r) {
|
|
172
|
+
return smoothstep(r, r - 0.035, length(p));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
float fftSample(float t) {
|
|
176
|
+
float index = clamp(floor(t * 31.0), 0.0, 31.0);
|
|
177
|
+
int i = int(index);
|
|
178
|
+
return u_fft[i];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
void main() {
|
|
182
|
+
vec2 uv = gl_FragCoord.xy / max(u_resolution.xy, vec2(1.0));
|
|
183
|
+
vec2 p = uv * 2.0 - 1.0;
|
|
184
|
+
p.x *= u_resolution.x / max(u_resolution.y, 1.0);
|
|
185
|
+
|
|
186
|
+
float visualGain = max(u_visual_gain, 1.0);
|
|
187
|
+
float globalWobble = max(u_wobble_amount, 1.0);
|
|
188
|
+
float amp = clamp(u_amplitude * visualGain * 2.4, 0.0, 2.0);
|
|
189
|
+
float bass = clamp(u_bass * 2.2, 0.0, 2.0);
|
|
190
|
+
float pulse = max(u_beat_pulse, u_beat);
|
|
191
|
+
|
|
192
|
+
float angle = atan(p.y, p.x) / 6.28318530718 + 0.5;
|
|
193
|
+
float spectrum = fftSample(angle);
|
|
194
|
+
|
|
195
|
+
float wobble = (0.10 + u_param_wobble + amp * 0.25 + bass * 0.18 + spectrum * 0.18) * globalWobble;
|
|
196
|
+
float warp = 1.0 + u_param_warp + u_mid * 2.4;
|
|
197
|
+
float distortion = 0.7 + u_param_distortion + u_high * 2.0;
|
|
198
|
+
|
|
199
|
+
p.x += sin(p.y * 5.0 * warp + u_time * (1.1 + u_high * 5.0) + spectrum * 2.5) * wobble;
|
|
200
|
+
p.y += cos(p.x * 4.0 * warp - u_time * (0.9 + u_bass * 4.0) + spectrum * 3.0) * wobble;
|
|
201
|
+
|
|
202
|
+
float r = 0.42 + bass * 0.16 + pulse * 0.12 + spectrum * 0.08;
|
|
203
|
+
float shape = blob(p, r);
|
|
204
|
+
float rings = sin(length(p) * (16.0 + distortion * 5.0) - u_time * (2.0 + u_high * 8.0));
|
|
205
|
+
float glow = smoothstep(0.25, 1.0, shape + rings * 0.12);
|
|
206
|
+
|
|
207
|
+
vec3 color = vec3(0.012, 0.018, 0.035);
|
|
208
|
+
color += vec3(0.12, 0.72, 1.0) * glow * (0.45 + amp);
|
|
209
|
+
color += vec3(0.95, 0.24, 0.82) * shape * (0.20 + bass);
|
|
210
|
+
color += vec3(1.0, 0.95, 0.8) * pulse * 0.16;
|
|
211
|
+
|
|
212
|
+
outColor = vec4(color, 1.0);
|
|
213
|
+
}
|
|
148
214
|
`,
|
|
149
215
|
audio_bars: `#version 300 es
|
|
150
216
|
precision mediump float;
|
|
@@ -200,12 +266,324 @@ void main() {
|
|
|
200
266
|
barColor *= 0.65 + 0.75 * (lowW * u_bass + midW * u_mid + highW * u_high + u_amplitude * 0.35);
|
|
201
267
|
barColor += vec3(0.9, 0.95, 1.0) * topGlow;
|
|
202
268
|
|
|
203
|
-
float
|
|
204
|
-
vec3 color = bg + barColor *
|
|
269
|
+
float barFill = barMask * fill;
|
|
270
|
+
vec3 color = bg + barColor * barFill;
|
|
205
271
|
color += barColor * barMask * topGlow * 0.45;
|
|
206
272
|
|
|
207
273
|
outColor = vec4(color, 1.0);
|
|
208
274
|
}
|
|
275
|
+
`,
|
|
276
|
+
ruby_crystal: `#version 300 es
|
|
277
|
+
precision mediump float;
|
|
278
|
+
uniform vec2 u_resolution;
|
|
279
|
+
uniform float u_time;
|
|
280
|
+
uniform float u_amplitude;
|
|
281
|
+
uniform float u_bass;
|
|
282
|
+
uniform float u_mid;
|
|
283
|
+
uniform float u_high;
|
|
284
|
+
uniform float u_beat;
|
|
285
|
+
uniform float u_beat_pulse;
|
|
286
|
+
uniform float u_bpm;
|
|
287
|
+
uniform float u_param_facets;
|
|
288
|
+
uniform float u_param_refraction;
|
|
289
|
+
uniform float u_global_intensity;
|
|
290
|
+
uniform float u_global_color;
|
|
291
|
+
out vec4 outColor;
|
|
292
|
+
|
|
293
|
+
const float PI = 3.14159265359;
|
|
294
|
+
const float TAU = 6.28318530718;
|
|
295
|
+
|
|
296
|
+
float sdDiamond(vec2 p) {
|
|
297
|
+
p = abs(p);
|
|
298
|
+
return p.x * 0.78 + p.y - 0.72;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
float line(float value, float width) {
|
|
302
|
+
return 1.0 - smoothstep(0.0, width, abs(value));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
vec3 rubyPalette(float t) {
|
|
306
|
+
vec3 deep = vec3(0.12, 0.00, 0.035);
|
|
307
|
+
vec3 red = vec3(0.92, 0.05, 0.18);
|
|
308
|
+
vec3 pink = vec3(1.00, 0.28, 0.52);
|
|
309
|
+
vec3 white = vec3(1.00, 0.86, 0.75);
|
|
310
|
+
vec3 hot = mix(red, pink, smoothstep(0.18, 0.78, t));
|
|
311
|
+
return mix(deep, mix(hot, white, smoothstep(0.72, 1.0, t)), smoothstep(0.0, 1.0, t));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
void main() {
|
|
315
|
+
vec2 uv = gl_FragCoord.xy / max(u_resolution.xy, vec2(1.0));
|
|
316
|
+
vec2 p = uv * 2.0 - 1.0;
|
|
317
|
+
p.x *= u_resolution.x / max(u_resolution.y, 1.0);
|
|
318
|
+
float globalIntensity = 1.0 + clamp(u_global_intensity, 0.0, 1.0) * 0.5;
|
|
319
|
+
float globalColor = clamp(u_global_color, 0.0, 1.0);
|
|
320
|
+
|
|
321
|
+
float facets = clamp(max(u_param_facets, 6.0), 4.0, 12.0);
|
|
322
|
+
float refraction = max(u_param_refraction, 0.35);
|
|
323
|
+
float tempo = max(u_bpm, 120.0) / 60.0;
|
|
324
|
+
float pulse = max(u_beat, u_beat_pulse);
|
|
325
|
+
float angle = atan(p.y, p.x);
|
|
326
|
+
float radius = length(p);
|
|
327
|
+
float spin = angle + u_time * (0.08 + tempo * 0.012) + u_mid * 0.55;
|
|
328
|
+
vec2 warped = p;
|
|
329
|
+
warped += vec2(cos(spin * facets), sin(spin * (facets - 1.0))) * (0.025 + u_bass * 0.055);
|
|
330
|
+
|
|
331
|
+
float body = 1.0 - smoothstep(-0.04, 0.05, sdDiamond(warped * (0.95 - pulse * 0.08)));
|
|
332
|
+
float edge = line(sdDiamond(warped), 0.035 + pulse * 0.02);
|
|
333
|
+
float facetLines = line(sin(spin * facets + radius * (8.0 + u_high * 8.0)), 0.06);
|
|
334
|
+
float crossCut = line(warped.x + warped.y * 0.42, 0.018) + line(warped.x - warped.y * 0.42, 0.018);
|
|
335
|
+
float sparkle = pow(max(0.0, sin(spin * 3.0 + radius * 18.0 - u_time * (1.4 + u_high * 5.0))), 18.0);
|
|
336
|
+
float light = clamp(body * (0.22 + facetLines * 0.34 + crossCut * 0.18) + edge * 0.9 + sparkle * (0.4 + u_high), 0.0, 1.5);
|
|
337
|
+
float glow = exp(-abs(sdDiamond(warped)) * 8.0) * (0.18 + u_bass * 0.35 + pulse * 0.45);
|
|
338
|
+
|
|
339
|
+
vec3 bg = vec3(0.018, 0.006, 0.018) + vec3(0.05, 0.00, 0.045) * (1.0 - smoothstep(0.0, 1.35, radius));
|
|
340
|
+
vec3 accent = mix(vec3(1.0, 0.18, 0.32), vec3(0.12, 0.72, 1.0), globalColor);
|
|
341
|
+
vec3 color = bg;
|
|
342
|
+
color += rubyPalette(light + refraction * u_amplitude * 0.28) * body * globalIntensity;
|
|
343
|
+
color += accent * glow * globalIntensity;
|
|
344
|
+
color += mix(vec3(1.0, 0.82, 0.70), accent, 0.35) * sparkle * (0.4 + u_high) * globalIntensity;
|
|
345
|
+
outColor = vec4(color, 1.0);
|
|
346
|
+
}
|
|
347
|
+
`,
|
|
348
|
+
starfield: `#version 300 es
|
|
349
|
+
precision mediump float;
|
|
350
|
+
uniform vec2 u_resolution;
|
|
351
|
+
uniform float u_time;
|
|
352
|
+
uniform float u_amplitude;
|
|
353
|
+
uniform float u_bass;
|
|
354
|
+
uniform float u_mid;
|
|
355
|
+
uniform float u_high;
|
|
356
|
+
uniform float u_beat;
|
|
357
|
+
uniform float u_global_intensity;
|
|
358
|
+
uniform float u_global_color;
|
|
359
|
+
out vec4 outColor;
|
|
360
|
+
|
|
361
|
+
float hash11(float p) {
|
|
362
|
+
return fract(sin(p * 127.1) * 43758.5453123);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
float star(vec2 p, float size) {
|
|
366
|
+
float d = length(p);
|
|
367
|
+
float core = 1.0 - smoothstep(0.0, size, d);
|
|
368
|
+
float rays = 1.0 - smoothstep(0.0, size * 3.0, abs(p.x) * abs(p.y));
|
|
369
|
+
return core + rays * 0.08;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
void main() {
|
|
373
|
+
vec2 uv = gl_FragCoord.xy / max(u_resolution.xy, vec2(1.0));
|
|
374
|
+
vec2 p = uv * 2.0 - 1.0;
|
|
375
|
+
p.x *= u_resolution.x / max(u_resolution.y, 1.0);
|
|
376
|
+
float globalIntensity = 1.0 + clamp(u_global_intensity, 0.0, 1.0) * 0.5;
|
|
377
|
+
float globalColor = clamp(u_global_color, 0.0, 1.0);
|
|
378
|
+
vec3 accentA = mix(vec3(0.20, 0.72, 1.0), vec3(1.0, 0.26, 0.58), globalColor);
|
|
379
|
+
vec3 accentB = mix(vec3(1.0, 0.26, 0.58), vec3(1.0, 0.78, 0.24), globalColor);
|
|
380
|
+
vec3 color = vec3(0.004, 0.008, 0.018);
|
|
381
|
+
color += vec3(0.015, 0.02, 0.05) * (1.0 - smoothstep(0.0, 1.4, length(p)));
|
|
382
|
+
|
|
383
|
+
float speed = 0.10 + u_bass * 0.65 + u_amplitude * 0.25;
|
|
384
|
+
for (int i = 0; i < 56; i++) {
|
|
385
|
+
float fi = float(i);
|
|
386
|
+
vec2 seed = vec2(hash11(fi + 11.0), hash11(fi + 37.0)) * 2.0 - 1.0;
|
|
387
|
+
float depth = fract(hash11(fi + 73.0) - u_time * speed * (0.35 + hash11(fi + 5.0)));
|
|
388
|
+
vec2 pos = seed / max(depth, 0.08);
|
|
389
|
+
float size = (1.0 - depth) * (0.004 + u_high * 0.011 + u_beat * 0.006);
|
|
390
|
+
float trail = star(p - pos, size);
|
|
391
|
+
vec3 tint = mix(accentA, accentB, hash11(fi + 101.0));
|
|
392
|
+
color += tint * trail * (0.18 + (1.0 - depth) * 0.85 + u_beat * 0.5) * globalIntensity;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
float warp = sin(length(p) * (12.0 + u_mid * 8.0) - u_time * (1.0 + u_high * 3.0));
|
|
396
|
+
color += vec3(0.08, 0.10, 0.18) * smoothstep(0.94, 1.0, warp) * (0.08 + u_mid * 0.22);
|
|
397
|
+
outColor = vec4(color, 1.0);
|
|
398
|
+
}
|
|
399
|
+
`,
|
|
400
|
+
waveform_ribbon: `#version 300 es
|
|
401
|
+
precision mediump float;
|
|
402
|
+
uniform vec2 u_resolution;
|
|
403
|
+
uniform float u_time;
|
|
404
|
+
uniform float u_amplitude;
|
|
405
|
+
uniform float u_bass;
|
|
406
|
+
uniform float u_mid;
|
|
407
|
+
uniform float u_high;
|
|
408
|
+
uniform float u_beat;
|
|
409
|
+
uniform float u_beat_pulse;
|
|
410
|
+
uniform float u_fft[32];
|
|
411
|
+
uniform float u_global_intensity;
|
|
412
|
+
uniform float u_global_color;
|
|
413
|
+
out vec4 outColor;
|
|
414
|
+
|
|
415
|
+
float fftSample(float t) {
|
|
416
|
+
float index = clamp(floor(t * 31.0), 0.0, 31.0);
|
|
417
|
+
return u_fft[int(index)];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
float line(float y, float center, float width) {
|
|
421
|
+
return 1.0 - smoothstep(0.0, width, abs(y - center));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
void main() {
|
|
425
|
+
vec2 uv = gl_FragCoord.xy / max(u_resolution.xy, vec2(1.0));
|
|
426
|
+
float globalIntensity = 1.0 + clamp(u_global_intensity, 0.0, 1.0) * 0.5;
|
|
427
|
+
float globalColor = clamp(u_global_color, 0.0, 1.0);
|
|
428
|
+
float spectrum = fftSample(uv.x);
|
|
429
|
+
float phase = uv.x * 18.8495559215 + u_time * (1.2 + u_mid * 3.5);
|
|
430
|
+
float center = 0.5 + sin(phase) * (0.035 + u_bass * 0.08) + sin(phase * 0.43 - u_time) * 0.035;
|
|
431
|
+
float width = 0.018 + u_amplitude * 0.055 + spectrum * 0.16 + u_beat_pulse * 0.025;
|
|
432
|
+
float primary = line(uv.y, center, width);
|
|
433
|
+
float echoA = line(uv.y, center + 0.16 + u_high * 0.08, width * 0.62);
|
|
434
|
+
float echoB = line(uv.y, center - 0.15 - u_bass * 0.06, width * 0.58);
|
|
435
|
+
float grid = smoothstep(0.012, 0.0, abs(fract(uv.x * 32.0) - 0.5)) * 0.08;
|
|
436
|
+
|
|
437
|
+
vec3 bg = vec3(0.006, 0.012, 0.026);
|
|
438
|
+
bg += vec3(0.02, 0.03, 0.055) * smoothstep(0.9, 0.0, abs(uv.y - 0.5));
|
|
439
|
+
vec3 primaryColor = mix(vec3(0.08, 0.92, 1.0), vec3(1.0, 0.18, 0.58), globalColor);
|
|
440
|
+
vec3 echoColor = mix(vec3(1.0, 0.18, 0.58), vec3(0.95, 0.78, 0.28), globalColor);
|
|
441
|
+
vec3 bassColor = mix(vec3(0.95, 0.78, 0.28), vec3(0.22, 0.78, 1.0), globalColor);
|
|
442
|
+
vec3 ribbon = primaryColor * primary;
|
|
443
|
+
ribbon += echoColor * echoA * (0.5 + u_high);
|
|
444
|
+
ribbon += bassColor * echoB * (0.35 + u_bass);
|
|
445
|
+
ribbon += vec3(0.7, 0.85, 1.0) * primary * u_beat * 0.35;
|
|
446
|
+
|
|
447
|
+
vec3 color = bg + ribbon * (0.65 + spectrum * 0.75 + u_amplitude * 0.5) * globalIntensity;
|
|
448
|
+
color += vec3(0.18, 0.28, 0.44) * grid * (0.2 + spectrum);
|
|
449
|
+
outColor = vec4(color, 1.0);
|
|
450
|
+
}
|
|
451
|
+
`,
|
|
452
|
+
unyo_geometry: `#version 300 es
|
|
453
|
+
precision mediump float;
|
|
454
|
+
|
|
455
|
+
uniform vec2 u_resolution;
|
|
456
|
+
uniform float u_time;
|
|
457
|
+
uniform float u_amplitude;
|
|
458
|
+
uniform float u_bass;
|
|
459
|
+
uniform float u_mid;
|
|
460
|
+
uniform float u_high;
|
|
461
|
+
uniform float u_beat;
|
|
462
|
+
uniform float u_beat_pulse;
|
|
463
|
+
uniform float u_bpm;
|
|
464
|
+
uniform float u_fft[32];
|
|
465
|
+
uniform float u_param_seed;
|
|
466
|
+
uniform float u_param_sides;
|
|
467
|
+
uniform float u_param_scale;
|
|
468
|
+
uniform float u_param_wobble;
|
|
469
|
+
uniform float u_param_twist;
|
|
470
|
+
uniform float u_param_pulse;
|
|
471
|
+
uniform float u_param_kick;
|
|
472
|
+
uniform float u_param_snare;
|
|
473
|
+
uniform float u_param_line_glow;
|
|
474
|
+
|
|
475
|
+
out vec4 outColor;
|
|
476
|
+
|
|
477
|
+
const float PI = 3.14159265359;
|
|
478
|
+
const float TAU = 6.28318530718;
|
|
479
|
+
|
|
480
|
+
float hash11(float p) {
|
|
481
|
+
return fract(sin(p * 127.1) * 43758.5453123);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
float fftSample(float t) {
|
|
485
|
+
float index = clamp(floor(fract(t) * 31.0), 0.0, 31.0);
|
|
486
|
+
return u_fft[int(index)];
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
mat2 rotate2d(float angle) {
|
|
490
|
+
float s = sin(angle);
|
|
491
|
+
float c = cos(angle);
|
|
492
|
+
return mat2(c, -s, s, c);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
float sdRegularPolygon(vec2 p, float radius, float sides) {
|
|
496
|
+
float angle = PI / sides;
|
|
497
|
+
float sector = TAU / sides;
|
|
498
|
+
vec2 axis = vec2(cos(angle), sin(angle));
|
|
499
|
+
float folded = mod(atan(p.x, p.y), sector) - angle;
|
|
500
|
+
vec2 q = length(p) * vec2(cos(folded), abs(sin(folded)));
|
|
501
|
+
return dot(q - vec2(radius, 0.0), axis);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
vec3 palette(float t) {
|
|
505
|
+
vec3 cyan = vec3(0.02, 0.88, 0.98);
|
|
506
|
+
vec3 pink = vec3(1.00, 0.20, 0.58);
|
|
507
|
+
vec3 amber = vec3(1.00, 0.76, 0.22);
|
|
508
|
+
vec3 green = vec3(0.26, 0.96, 0.54);
|
|
509
|
+
vec3 first = mix(cyan, pink, smoothstep(0.0, 0.45, t));
|
|
510
|
+
vec3 second = mix(amber, green, smoothstep(0.55, 1.0, t));
|
|
511
|
+
return mix(first, second, smoothstep(0.35, 0.75, t));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
void main() {
|
|
515
|
+
vec2 uv = gl_FragCoord.xy / max(u_resolution.xy, vec2(1.0));
|
|
516
|
+
vec2 p = uv * 2.0 - 1.0;
|
|
517
|
+
p.x *= u_resolution.x / max(u_resolution.y, 1.0);
|
|
518
|
+
|
|
519
|
+
float seed = floor(max(u_param_seed, 0.0));
|
|
520
|
+
float sideDrift = floor(mod(seed, 5.0));
|
|
521
|
+
float sides = clamp(floor(max(u_param_sides, 7.0) + sideDrift - 2.0), 4.0, 12.0);
|
|
522
|
+
float scale = clamp(max(u_param_scale, 0.95), 0.55, 1.45);
|
|
523
|
+
float rawPulse = clamp(max(max(u_param_pulse, u_beat_pulse), u_beat), 0.0, 1.8);
|
|
524
|
+
float rawKick = clamp(max(u_param_kick, u_bass * 1.15), 0.0, 1.8);
|
|
525
|
+
float rawSnare = clamp(max(u_param_snare, u_high * 0.9), 0.0, 1.4);
|
|
526
|
+
float soundEnergy = max(max(u_amplitude, rawKick * 0.7), rawSnare * 0.55);
|
|
527
|
+
float soundGate = smoothstep(0.006, 0.035, soundEnergy);
|
|
528
|
+
float motionTime = u_time * soundGate;
|
|
529
|
+
float pulse = rawPulse * soundGate;
|
|
530
|
+
float kick = rawKick * soundGate;
|
|
531
|
+
float snare = rawSnare * soundGate;
|
|
532
|
+
float drumHit = max(pulse, kick);
|
|
533
|
+
float wobble = max(u_param_wobble, 0.3) + kick * 0.35;
|
|
534
|
+
float twist = max(u_param_twist, 0.35) + snare * 0.38;
|
|
535
|
+
float lineGlow = max(u_param_line_glow, 0.16);
|
|
536
|
+
float tempo = max(u_bpm, 120.0) / 60.0;
|
|
537
|
+
|
|
538
|
+
vec2 bodyDrift = vec2(
|
|
539
|
+
sin(motionTime * (0.65 + tempo * 0.08) + seed * 1.7),
|
|
540
|
+
cos(motionTime * (0.58 + tempo * 0.07) + seed * 1.1)
|
|
541
|
+
) * soundGate * (0.035 + drumHit * 0.075);
|
|
542
|
+
p -= bodyDrift;
|
|
543
|
+
|
|
544
|
+
float radius = length(p);
|
|
545
|
+
float angle = atan(p.y, p.x);
|
|
546
|
+
float spectrum = fftSample(angle / TAU + 0.5 + seed * 0.017);
|
|
547
|
+
float activeSpectrum = spectrum * soundGate;
|
|
548
|
+
float breathing = 1.0 + drumHit * 0.22 + u_bass * soundGate * 0.12 + activeSpectrum * 0.09;
|
|
549
|
+
float organic = sin(angle * (sides + 1.0) + motionTime * (1.0 + u_mid * 2.2) + seed);
|
|
550
|
+
organic += sin(angle * (sides * 2.0 - 1.0) - motionTime * (0.8 + u_high * 2.8 + snare * 1.4) + seed * 0.7) * (0.55 + kick * 0.45);
|
|
551
|
+
organic += activeSpectrum * (1.1 + drumHit * 0.7);
|
|
552
|
+
|
|
553
|
+
float twistAngle = radius * twist * (0.9 + u_mid * 1.7 + drumHit * 0.55) + organic * wobble * 0.14;
|
|
554
|
+
vec2 warped = rotate2d(twistAngle) * p;
|
|
555
|
+
warped *= 1.0 + organic * wobble * 0.095 + kick * 0.025;
|
|
556
|
+
|
|
557
|
+
float baseRadius = 0.60 * scale * breathing;
|
|
558
|
+
float d = sdRegularPolygon(warped, baseRadius, sides);
|
|
559
|
+
float inside = 1.0 - smoothstep(-0.006, 0.024, d);
|
|
560
|
+
float edge = smoothstep(0.025, 0.0, abs(d));
|
|
561
|
+
float glow = exp(-abs(d) * 13.0) * (0.18 + drumHit * 0.68 + u_amplitude * 0.32);
|
|
562
|
+
|
|
563
|
+
float warpedAngle = atan(warped.y, warped.x);
|
|
564
|
+
float warpedRadius = length(warped);
|
|
565
|
+
float spokes = smoothstep(0.06, 0.0, abs(sin(warpedAngle * sides + motionTime * (0.7 + u_high * 1.6 + snare * 1.2))));
|
|
566
|
+
float rings = smoothstep(0.055, 0.0, abs(sin(warpedRadius * (24.0 + sides * 1.7 + kick * 7.0) - motionTime * tempo * (1.7 + drumHit * 1.1))));
|
|
567
|
+
float core = smoothstep(0.14, 0.0, abs(warpedRadius - baseRadius * (0.32 + drumHit * 0.11)));
|
|
568
|
+
float innerLines = inside * max(spokes * 0.72, max(rings * 0.5, core * 0.62)) * lineGlow;
|
|
569
|
+
|
|
570
|
+
vec3 bg = vec3(0.012, 0.016, 0.028);
|
|
571
|
+
bg += vec3(0.018, 0.024, 0.040) * smoothstep(1.25, 0.05, length(p));
|
|
572
|
+
bg += vec3(0.04, 0.028, 0.065) * (0.06 + u_mid * 0.12) * smoothstep(1.0, 0.0, radius);
|
|
573
|
+
|
|
574
|
+
vec3 shapeColor = palette(fract(seed * 0.071 + warpedAngle / TAU + u_high * 0.16));
|
|
575
|
+
vec3 innerColor = palette(fract(seed * 0.11 + 0.45 + warpedRadius));
|
|
576
|
+
shapeColor *= 0.78 + u_amplitude * 0.42 + drumHit * 0.46 + snare * 0.18;
|
|
577
|
+
|
|
578
|
+
vec3 color = bg;
|
|
579
|
+
color += shapeColor * glow;
|
|
580
|
+
color += shapeColor * edge * (1.2 + drumHit * 0.72);
|
|
581
|
+
color += innerColor * innerLines * (0.8 + u_high * 0.75 + snare * 0.35);
|
|
582
|
+
color += shapeColor * inside * 0.10;
|
|
583
|
+
color += vec3(1.0, 0.96, 0.86) * drumHit * 0.06;
|
|
584
|
+
|
|
585
|
+
outColor = vec4(color, 1.0);
|
|
586
|
+
}
|
|
209
587
|
`,
|
|
210
588
|
glitch_flash: `#version 300 es
|
|
211
589
|
precision mediump float;
|
|
@@ -76,6 +76,58 @@ void main() {
|
|
|
76
76
|
float flicker = 0.96 + 0.04 * sin(u_time * 4.0);
|
|
77
77
|
outColor = vec4(base.rgb * flicker, base.a);
|
|
78
78
|
}
|
|
79
|
+
`,
|
|
80
|
+
motion_blur: `#version 300 es
|
|
81
|
+
precision mediump float;
|
|
82
|
+
in vec2 v_uv;
|
|
83
|
+
uniform sampler2D u_texture;
|
|
84
|
+
uniform vec2 u_resolution;
|
|
85
|
+
uniform float u_intensity;
|
|
86
|
+
out vec4 outColor;
|
|
87
|
+
|
|
88
|
+
void main() {
|
|
89
|
+
vec2 texel = 1.0 / max(u_resolution, vec2(1.0));
|
|
90
|
+
vec2 center = v_uv - vec2(0.5);
|
|
91
|
+
float radius = length(center);
|
|
92
|
+
vec2 direction = normalize(center + vec2(0.001));
|
|
93
|
+
float amount = (2.0 + u_intensity * 12.0) * radius;
|
|
94
|
+
vec4 color = texture(u_texture, v_uv) * 0.36;
|
|
95
|
+
color += texture(u_texture, v_uv - direction * texel * amount) * 0.28;
|
|
96
|
+
color += texture(u_texture, v_uv - direction * texel * amount * 2.0) * 0.20;
|
|
97
|
+
color += texture(u_texture, v_uv - direction * texel * amount * 3.0) * 0.12;
|
|
98
|
+
color += texture(u_texture, v_uv - direction * texel * amount * 4.0) * 0.04;
|
|
99
|
+
outColor = color;
|
|
100
|
+
}
|
|
101
|
+
`,
|
|
102
|
+
crt: `#version 300 es
|
|
103
|
+
precision mediump float;
|
|
104
|
+
in vec2 v_uv;
|
|
105
|
+
uniform sampler2D u_texture;
|
|
106
|
+
uniform vec2 u_resolution;
|
|
107
|
+
uniform float u_time;
|
|
108
|
+
uniform float u_intensity;
|
|
109
|
+
out vec4 outColor;
|
|
110
|
+
|
|
111
|
+
float rand(vec2 p) {
|
|
112
|
+
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void main() {
|
|
116
|
+
vec2 centered = v_uv * 2.0 - 1.0;
|
|
117
|
+
float curvature = 0.08 * u_intensity;
|
|
118
|
+
vec2 warped = centered * (1.0 + curvature * dot(centered, centered));
|
|
119
|
+
vec2 uv = warped * 0.5 + 0.5;
|
|
120
|
+
vec4 base = texture(u_texture, clamp(uv, vec2(0.0), vec2(1.0)));
|
|
121
|
+
float inBounds = step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0);
|
|
122
|
+
float scanline = 0.86 + 0.14 * sin(v_uv.y * max(u_resolution.y, 1.0) * 3.14159);
|
|
123
|
+
float vignette = 1.0 - smoothstep(0.25, 0.95, length(centered));
|
|
124
|
+
float staticNoise = (rand(v_uv * max(u_resolution, vec2(1.0)) + vec2(u_time)) - 0.5) * 0.08 * u_intensity;
|
|
125
|
+
vec3 color = base.rgb * scanline * vignette;
|
|
126
|
+
color.r *= 1.0 + 0.04 * u_intensity;
|
|
127
|
+
color.b *= 1.0 - 0.05 * u_intensity;
|
|
128
|
+
color += staticNoise;
|
|
129
|
+
outColor = vec4(color * inBounds, base.a * inBounds);
|
|
130
|
+
}
|
|
79
131
|
`
|
|
80
132
|
};
|
|
81
133
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const DEFAULT_SIZE = 64;
|
|
2
|
+
const HASH_OFFSET = 0x811c9dc5;
|
|
3
|
+
const HASH_PRIME = 0x01000193;
|
|
4
|
+
|
|
5
|
+
export const lineSegmentsFingerprint = (points, {
|
|
6
|
+
width = DEFAULT_SIZE,
|
|
7
|
+
height = DEFAULT_SIZE,
|
|
8
|
+
} = {}) => {
|
|
9
|
+
const pixels = rasterizeLineSegments(points, { width, height });
|
|
10
|
+
let hash = HASH_OFFSET;
|
|
11
|
+
|
|
12
|
+
for (const value of pixels) {
|
|
13
|
+
hash ^= value;
|
|
14
|
+
hash = Math.imul(hash, HASH_PRIME) >>> 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return hash.toString(16).padStart(8, "0");
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const rasterizeLineSegments = (points, {
|
|
21
|
+
width = DEFAULT_SIZE,
|
|
22
|
+
height = DEFAULT_SIZE,
|
|
23
|
+
} = {}) => {
|
|
24
|
+
const safeWidth = clampInt(width, 8, 512);
|
|
25
|
+
const safeHeight = clampInt(height, 8, 512);
|
|
26
|
+
const pixels = new Uint8Array(safeWidth * safeHeight);
|
|
27
|
+
const input = Array.isArray(points) || ArrayBuffer.isView(points) ? Array.from(points) : [];
|
|
28
|
+
|
|
29
|
+
for (let index = 0; index + 3 < input.length; index += 4) {
|
|
30
|
+
const x1 = normalizedToPixel(input[index], safeWidth);
|
|
31
|
+
const y1 = normalizedToPixel(-input[index + 1], safeHeight);
|
|
32
|
+
const x2 = normalizedToPixel(input[index + 2], safeWidth);
|
|
33
|
+
const y2 = normalizedToPixel(-input[index + 3], safeHeight);
|
|
34
|
+
drawLine(pixels, safeWidth, safeHeight, x1, y1, x2, y2);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return pixels;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const drawLine = (pixels, width, height, x1, y1, x2, y2) => {
|
|
41
|
+
const dx = Math.abs(x2 - x1);
|
|
42
|
+
const dy = Math.abs(y2 - y1);
|
|
43
|
+
const steps = Math.max(dx, dy, 1);
|
|
44
|
+
|
|
45
|
+
for (let step = 0; step <= steps; step += 1) {
|
|
46
|
+
const amount = step / steps;
|
|
47
|
+
const x = Math.round(x1 + (x2 - x1) * amount);
|
|
48
|
+
const y = Math.round(y1 + (y2 - y1) * amount);
|
|
49
|
+
if (x < 0 || y < 0 || x >= width || y >= height) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
pixels[y * width + x] = 255;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const normalizedToPixel = (value, size) => {
|
|
57
|
+
const normalized = (clamp(Number(value) || 0, -1, 1) + 1) * 0.5;
|
|
58
|
+
return Math.round(normalized * (size - 1));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const clampInt = (value, min, max) => {
|
|
62
|
+
const numeric = Number(value);
|
|
63
|
+
if (!Number.isFinite(numeric)) return min;
|
|
64
|
+
return Math.round(Math.min(Math.max(numeric, min), max));
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|