Repository navigation
Conversation
Reusing one pixel-pack buffer made Chromium log "READ-usage buffer was written, then fenced, but written again before being read back" on every read, even though each read waited for its fence (tried with getSyncParameter and clientWaitSync: 19 warnings in 20 reads). The smoke test counts console warnings as errors. Each read now gets its own buffer, deleted once read: 0 warnings, same frame rate. Refs CaYatur#621
perf: stop the visualizer slowing down as settings grow (CaYatur#621)
Every layer showed all of its settings at once; on a real six-layer scene the Layers card was 4,356 px tall. Layers now arrive collapsed with a one-line summary (kind, source, blend, opacity) and open on a click, remembered per machine in localStorage rather than the settings file. An open layer lays its controls out in columns sized to the card and its five sub-sections are a tab strip. A newly added layer opens; a locked one cannot. The add buttons sit in an equal-cell grid. Same scene with everything collapsed: 612 px. Refs CaYatur#622
- A drop-down beside its label no longer takes the whole row and sits 7 px lower, which broke labels over two lines. - Cards stack under the shortest column in order (4 px grid rows spanned by each card's height, re-measured on resize) instead of leaving a hole under a short card; a lone half-width card between wide ones takes the full row. - The colour preset grid uses 4, 6, 7, 8 or 10 columns, so its groups of 10 and 6 never end with a single tile on its own row. Refs CaYatur#622 - Number boxes in the timeline and the clip deck had no style and drew as white browser defaults; they now match the panel, and the page declares a dark color-scheme for controls the browser draws itself.
ui: balanced panel — collapsible layers, masonry cards, aligned rows (CaYatur#622)
MilkDrop's expression compiler (Nullsoft ns-eel2, the BeatDrop copy used throughout) was built from source into a comparison tool outside the repo, and the same expressions were run in it and in the engine. With fidelity on the engine now follows it: - a preprocessor rewrites = += == < > && || % ^ ! ?: [ ] into calls, finding each operand by scanning the text, before a small grammar of + - * / & | reads the rest; this is where MilkDrop's precedence comes from (-2^2 = -4, 3*-7%4 = -9, 2^2^3 = 64, 1<2<3 = 1<(2<3), an assignment binding to the term just before it); ';' inside parentheses becomes a sequence or a space; - one statement that does not compile drops the whole block, as in MilkDrop; integer literals clamp to 2^31-1, 1e3 and 0x1F do not compile, 1.5e-2 is 1.5 - 2, a lone '.' is 0, $pi $e $phi $x $'c' work, a function name without parentheses is an error; - no guards: division by zero is infinity or NaN and flows through the expression until a plain assignment writes 0 for infinity, NaN and denormals; compound assignments keep them; - x87 chop rounding for + - * / and sqrt, detected exactly with error terms; overflow gives the largest double; NaN compares the x87 way; sin/cos past 2^63 return the argument, tan gives NaN; sigmoid guarded like MilkDrop's; invsqrt added; a variable operand is read after the other operand is computed. Corpus: all 10,757 unique blocks agree on compiling; run four frames with the same inputs, 17 of the 9,486 rand-free blocks differ, all from the last bit of x87's atan2/sin/cos. Fidelity off is unchanged. Equations cost about 20% more per frame. Refs CaYatur#580, CaYatur#560 ROADMAP records the rules found with the reference build, corrects the earlier notes on division by zero and on '-7 % 3', and lists what is still different (x87 transcendental last bit, NaN sign bit, rand's generator).
Since CaYatur#621 a disposed layer canvas drops to zero size. The MilkDrop context-loss probe could pick up the previous scene's layer before the pushed settings were applied and then threw on the zero-size canvas; the surface probe looked once after a fixed delay and missed a late rebuild. The MilkDrop probe now needs the same attached layer on two polls in a row and reports a disposed canvas instead of throwing, and the surface probe polls for up to four seconds. Refs CaYatur#621
MilkDrop: read and compute equations the way MilkDrop 2 does
A soak on the reporter's settings with exceptions counted showed the particles visualizer throwing a ReferenceError on every frame in the theme colour mode (COUNT was never defined; the pool size is MAX), 886 times in four minutes while Auto VJ had it on screen. The throw left the layer loop, so the layers above it, the effect chain and the lights were not drawn in those frames. The name is fixed, and LayerStack now catches a throwing layer, skips just that layer and logs the error once per kind:type. A sweep drawing every visualizer in custom/theme/rainbow and every background in the app found no other exception. Refs CaYatur#621
Drawing every visualizer in the app with exceptions counted also turned up, now and then during a scene change, drawImage throwing on a layer canvas of zero size: the same whole-frame loss. Since CaYatur#621 a disposed layer canvas drops to zero size, and a canvas made before the window has its size is born at zero. The compositor, layer masks and layer effects now skip a zero-size canvas. Refs CaYatur#621
…stage The self-test runs on the real settings. With Auto VJ on there, the panel switched the scene every few seconds and overwrote the scene the test had set up, so the measured MilkDrop layer was disposed between polls and steps failed at random (context loss, sprites, surfaces). Auto VJ is now turned off in the panel's in-memory copy at the start (nothing is written; the Auto VJ check further down turns it on and measures it). The per-mode canvas check also polls up to ~2 s instead of looking once after 260 ms: the opening scene can keep the window busy when the first setting arrives. Refs CaYatur#621
Visualizer: one layer that throws no longer drops the whole frame
Per-pixel code runs at every mesh point. On the reporter's preset (three pow/acos/sin/cos groups on a 64x48 mesh) it took 9.5 ms a frame in node; now 6.6 ms. Over 200 corpus presets: 1.05 -> 0.63 ms, more than paying back the exact arithmetic added for CaYatur#580. - toZero: one multiplication by (1 - 2^-53) for normal numbers, the bit path only for subnormals; checked equal on every exponent and 200k random bit patterns. - + - *: plain variables and numbers are read directly instead of through a closure call each; the read order of the MilkDrop reading (left variable after the right expression) is kept. - sin, cos, asin, acos, sqr, sqrt, sign, pow, min, max, above, below, abs, int, atan, atan2 get call sites of their own so V8 can inline them. - Preset.pixel caches its 14 variable indices instead of 24 Map lookups per mesh point; statements are no longer wrapped in a closure whose result was thrown away. Old and new engine side by side over all 10,347 corpus presets, fidelity on and off: frame, mesh, wave and shape outputs identical to the bit. Refs CaYatur#621, CaYatur#560
MilkDrop: equations take a third less time, results unchanged
Studio -> MilkDrop Preset Editor opens the preset on screen. Frame and pixel equations, the four custom waves and shapes, the warp and composite shaders, the main values and the whole file text each have a tab. A change reaches the running preset ~0.4 s after typing stops, as a new version loaded without a blend and without a history entry; automatic switching pauses while editing and is restored on close. Errors point at the preset's own line: each statement is parsed on its own the way MilkDrop reads the block (comments cut per line, lines glued, statements split at top-level ;) and reported where it starts, with the key (per_frame_14) and the preset's text, not the engine's rewritten code. Unclosed or extra parentheses and unknown functions are named; with fidelity on the list says MilkDrop drops the whole block. Shaders are compiled in the panel's own WebGL context and the compiler's line is mapped back by shared tokens. Sliders for zoom, warp, rotation, decay and echo say when the equations rewrite the value every frame, from itself or outright. Save as a New Preset writes a new library entry (later saves update it, New Copy makes another); the original never changes, and closing without saving brings it back. Only changed blocks and values are written; blocks are read with MilkDrop's own reader (parseMilkMd2 now hands out the raw lines). Over all 10,347 corpus files: unchanged rewrites are byte-identical, every block read equals MilkDrop's reading, and an edit changes exactly the edited block and values. Refs CaYatur#578, CaYatur#560
MilkDrop: an in-app preset editor
A Preset Format setting (Automatic, MilkDrop 2, MilkDrop 3), separate from fidelity: MilkDrop 3 rules read 16 custom wave and shape slots and carry q1-q64 from the frame equations to pixels, waves and shapes; MilkDrop 2 rules keep four slots and q1-q32. parseMilkMd2 takes the slot count, Preset takes the format, and the visualizer rebuilds the running preset only when its rules change. Automatic reads a preset with MilkDrop 3 rules when it enables or codes slots 6-16, uses q33-q64, or calls get_fft/get_fft_hz or reads mouse. in a shader. A fifth slot alone and wave modes 8+ are not counted: in the 10,347-preset corpus 7 and 5 files show them, all MilkDrop 2-era files whose authors saw MilkDrop 2 ignore them. Automatic reads no corpus preset as MilkDrop 3, and old and new engine agree to the bit on every corpus preset with fidelity on and off, except those 7 with fidelity off: the old reader drew the fifth slot MilkDrop 2 never reads. MilkDrop 3's hard cuts 1-6 with the thresholds and delays of its README, hidden (falling back to MilkDrop 2's) in MilkDrop 2 format. The editor's slot pickers follow the format; other slots' lines stay untouched. Not done, as nothing describes them (MilkDrop 3's public source is still MilkDrop 2.2-level and its presets ship only inside an installer): the eight new waveforms, get_fft and mouse in shaders, .milk2, the new transitions, hard cut 6's preset and 7's effect. Refs CaYatur#567, CaYatur#560
The context-loss probe measured the user's selected preset, and its 'picture came back' check depended on what that preset draws in silence: with a preset whose wave alpha is 0.001 the screen stayed dark after the feedback buffer was lost, and the step failed at random (brightest sample 0-66). It now loads the first builtin preset: three runs in a row pass with samples of 215-251 in both recovery paths. Refs CaYatur#621
MilkDrop: a MilkDrop 2 / MilkDrop 3 preset format
MilkDrop draws each border ring in clip space from 1 - size to 1 (the inner ring inside the outer), as four mitred trapezoids, with sizes left unclamped and the 0.001 alpha threshold applied to the raw value before the colour wraps (milkdropfs.cpp:3226-3284). The engine clamped the width to the screen, skipped a negative width and tested the wrapped alpha against 0.002. Running the corpus for 30 frames: 31 presets reach a negative outer width, 13 a negative inner one (a visible band on top of the outer ring in MilkDrop), 29 an alpha on different sides of the two thresholds, 7 widths above 1 (overlapping trapezoids). The geometry is a pure function (borderRings) and its tests sample the triangles for coverage instead of pinning source text. Centre darkening was checked and already matched. Refs CaYatur#580, CaYatur#560
MilkDrop: borders drawn with MilkDrop's geometry
MilkDrop applies each blur level's min/max in the horizontal pass (blur1_ps.fx) and writes to an 8-bit target, so brightness outside the range is clipped before the vertical blur. The engine scaled in the vertical pass on half-float targets and never clipped. With fidelity on it now scales in the horizontal pass and both passes saturate to 0..1. It shows in presets that read a blur level and narrow its range: 246 of the 7,379 corpus presets that read one. Checked and already matching: weights, paired tap distances, pass count, the progressive min/max chain and the edge darkening on the first vertical pass; MilkDrop's +1 texel offsets make up for Direct3D 9's half-pixel rule at a 2:1 reduction, so the net sample is the texel centre, where the engine samples. The 8-bit rounding is not reproduced. Refs CaYatur#580, CaYatur#560
MilkDrop: blur levels scaled in the horizontal pass and saturated
MilkDrop compares wrap with the transition's snap point in the fixed warp (milkdropfs.cpp:1785), not with 0.5: when only the old preset has a composite shader the snap point is -0.01 and the fixed warp always wraps, when only the new one does it is 1.01 and it never wraps. The engine used 0.5. Checked against WarpedBlit_NoShaders and RunPerFrameEquations and already matching: bilinear sampling times the vertex colour (decay through COLOR_NORM), no gamma, the blended and snapped value lists and the snap point rule; the echo orientation, the snap point's other use, was already covered. The per-frame decay stays frame-rate corrected: MilkDrop's default cap is 30 fps and the correction reproduces it at any rate. Refs CaYatur#580, CaYatur#560
MilkDrop: the fixed warp wraps by the transition's snap point
MilkDrop's composite mesh carries rad = |p| / |aspect| (1 at the corners) and ang = atan2(py, px) in 0..2pi with p = (2u-1, 2v-1) x aspect (UvToMathSpace). The engine had no divisor (1.15 at the corners on 16:9), no aspect in ang and a -pi..pi range; 1,276 corpus composite shaders read rad or ang. With fidelity on the translator now emits MilkDrop's formula (translate takes acc). The warp mesh's values differ on purpose in MilkDrop and already matched. scripts/milkdrop-compile-rate.js translates the way the engine does (fidelity on; --legacy for off): all 16,346 corpus stages compile. uv orientation is written up as not done: the source does not settle it (the warp mesh computes v ~ 1 for the top row and flips rows as it draws, the composite puts v = 0 at the top) and it needs a reference renderer, as does the whole-frame comparison. Refs CaYatur#580, CaYatur#560
MilkDrop: the composite stage's rad and ang by MilkDrop's formula
Auto VJ builds a new MilkDrop engine each time it switches a layer to MilkDrop. That frame measured ~130 ms on the reporter's settings (warm), 95 ms of it in the GL setup and 41-47 ms regenerating the six noise textures in JavaScript - identical every time, as the generator always starts from the same seed. The pixels are now kept per page (per fidelity mode) and only uploaded per engine: noise 4-7 ms, GL setup ~50 ms, the setup frame 80-120 ms. Refs CaYatur#621
MilkDrop: noise texture pixels generated once per page
The MilkDrop list and grid showed only the first 400 presets of the current filter. Above 200 presets the panel now draws only the rows in view (plus six on each side) between two spacer boxes that keep the scroll bar at full length; the row pitch and grid column count are measured from the drawn rows. Rows still in view are reused, and the grid asks thumbnails only for cells that exist. Checked in the app with 10,347 presets: last preset reachable, clicked preset stays highlighted and in view, redraw 32-41 ms, no exceptions. Refs CaYatur#635
…e recording
Review follow-up on this branch:
- The text renderer translated any flagged content, and nothing cleared
the flag, so a user who typed a dictionary word over a template line
("Sahne") would see it translated in English. Only the two template
strings are translated now; editing the text drops the flag, and
leaving the now-playing source keeps the placeholder in the UI
language as plain text.
- The live recorder records the admin preview surface, so the media
error text and dim would have been recorded. The panel exposes
isRecording() and the media layer hides the message while it is true.
Refs CaYatur#695
fix: outside review — Studio apply, locked layers, output overlays, narrow header
…highlight Findings from the second outside review (run on ffce67b), each checked in code first: - sv_patch_config wrote a value of a different type over an existing one: {"path":"layers","value":"x"} turned the layer list into a string and saved it. The new value must have the existing value's type (new keys and null stay free), and numbers must be finite. - MCP ignored layer locks. Like the panel, a locked layer cannot be removed, reordered or have its fields, position, settings or effects changed; show, solo, mute and lock still work, and a patch that unlocks ({locked:false}) may edit in the same call. Raw layers.N paths follow the same rule. - Auto VJ switched the type of a locked visualizer layer and wrote palette colours into it; locked layers are skipped now. - Studio's highlighter held comments behind a numeric index that the number rule matched again, so "// Shadertoy" showed as "0". The index is written in private-use characters. - A shader file that does not compile was imported silently; it is still saved (so it can be fixed) but now warns. - The panel's colour preset import took "#zzzzzz", "red" and 123; presets with an invalid colour are skipped and counted. - All six image pickers (logo, cover, sprites, clip deck, now playing, layers) accepted any file named as an image; the file is now decoded by the browser first. - The video library accepted a text file named .mp4; it checks the container header (ISO boxes, EBML, AVI) like the image library. - The file-write guard used path.resolve, so a symlink or junction into userData got past it; both sides are now resolved to real paths. - MilkDrop tags given to sv_save_preset only went into the file; the panel searches milkdropLibrary.tags, where they are now written too. - sv_set_visualizer_type returned the old Studio id for non-Studio types. - Template effects had no id and could not be targeted by effectId. - Arrows next to a locked layer looked enabled but did nothing; they are disabled with a reason. A layer whose Studio preset was deleted now says so in the panel. 13 new tests in tests/external-review-v2.test.js, all failing on the previous code. Video library fixtures now carry a real MP4 header. Refs CaYatur#695
ROADMAP entry: what was fixed, what was measured live and what was left unchanged with reasons (English template names, global controls on a locked layer, ignored extra arguments, non-video media paths). Test counts updated. Refs CaYatur#695
…mage check Review follow-up on this branch: - setEffective (used only by Auto VJ's palette switch and the dynamic theme) no longer writes into a locked layer, so both kinds of unattended automation respect the lock; the Auto VJ special case is gone and its status line says when visualizer layers are locked. Operator controls still reach the visible layer. - The type check in sv_patch_config refused nulls the app itself writes (display.id, layerStack.enabled, mapping.outputs.*). A scan of the defaults, all 72 templates and a real settings file found only null/value pairs; primitives and mapping outputs may be set to null, lists and objects may not. A guard test keeps the scan. - imageOk rejected a real image whose type the OS does not know (data:application/octet-stream); the browser decode alone decides. Measured live: a locked bars layer and its colour stayed through Auto VJ visualizer and palette switches while the global palette changed; the same layer switched once unlocked. Refs CaYatur#695
fix: second outside review — type checks, locks over MCP and Auto VJ, file checks
… hardlinks, edge arrows, import, ports, record saving - sv_patch_config writes under layers go through the sv_update_layer rules (flags, opacity/transform ranges, kind/type/blend); a missing field is typed by the default, a null field takes only a single value - file-write guard refuses files with other hard links - unknown tool arguments come back as 'ignored'; sv_set_autovj refuses a call with no known argument - video sniff checks the ISO box size against the file size - edge move arrows disabled (layers, item lists, timeline) - MilkDrop import skips empty files at scan and non-preset text at import - OSC/stream/OpenRGB port fields need a whole number in range - record panel keeps Snapshot/Start disabled while a file is being saved Refs CaYatur#695
… list on a copy Refs CaYatur#695
fix: third outside review — patch_config layer checks, null paths, hard links, edge arrows, import, ports, record saving
…fects, OSC port floor - sv_patch_config writes to postfx, visualizer, background, modulation and autovj are made on a copy and checked with the owning tool's rules; layers.N.postfx goes through the same effect check (known type, object params, ids given); only changed list items are checked - the transparent stream background is accepted as a background type - OSC port floor is 1 on Windows, 1024 elsewhere Refs CaYatur#695
fix: fourth outside review — section checks in patch_config, layer effects, OSC port floor
…all root, pack the window icon The installer registers a sparse identity package whose external location is the install folder; the taskbar then uses the package's Square44x44Logo and needs resources.pri there to find the unplated (transparent) variants. Also pack build/icon.ico (the installed build had no window icon) and refresh the shell icon cache after registering the identity. Refs CaYatur#695
fix: installed taskbar icon — identity resources.pri at the install root, pack the window icon
- card templates draw cover, title and artist from one Now Playing layer; the engine keeps the cover beside the text at the same gap in every aspect ratio (new nowplaying anchor:'group'); bars share the card's margin - remove film grain from every card ground - admin-preview-only placeholder text for the card when no track plays - new cards: Wave Card, Cover Ring, Stage Card - new group Music Backdrop: six plain, dimmed visualizers behind the card - README screenshot generator feeds the card a manual track Refs CaYatur#695
…the card's manual track Refs CaYatur#695
feat: rebuild Music Video cards (no grain, aligned cover and text), add Music Backdrop templates
…ard, new demo-visualizer gif - README shot generator gives the card an album-style cover (shared with the Now Playing shot) and fills the global manual track so Cover Ring's logo layer shows it - music video section text follows the one-card layout - demo-visualizer.gif replaced by the owner's new recording; alt text updated Refs CaYatur#695
docs: refresh music video screenshots, add Cover Ring and Stage Card, new demo-visualizer gif
- Add control to CHECKED_SECTIONS: control.osc.port follows the admin panel rule (integer 1-65535 on Windows, 1024-65535 elsewhere); enabled flags must be booleans, midi.deviceId a non-empty string, mappings an array of objects, osc.host a non-empty string; unknown keys rejected. - Add nowplaying to CHECKED_SECTIONS: anchor must be 'text' or 'group', the values the renderer supports. - Pass the host platform through the MCP server ctx. - Add tests (5 new, 2907 total) and update test counts in docs.
fix(mcp): validate control and nowplaying.anchor in sv_patch_config
- New 'backgrounds' kind/source: pool is the cyclable background modes from mode-catalog plus user Studio shader background presets (picked as 'preset:<id>'). Included in the 'all' rotation. - picks.backgrounds added and normalized (unknown ids dropped; old configs default to []). - Admin applies to enabled, unlocked background layers only; Studio presets set type 'custom' + presetId (custom.backgroundId outside the layer stack). Transparent overlay backgrounds are left untouched. - Auto VJ panel: source and pick list, TR/EN strings. - MCP sv_set_autovj: 'backgrounds' source and picks.backgrounds, unknown mode ids and missing Studio presets are rejected. - Tests: 9 new (2916 total); existing all-rotation expectations updated.
- New 'custom' source: autovj.customKinds picks which kinds take part (scenes, visualizers, palettes, backgrounds; default visualizers + backgrounds, normalized to unique kinds in canonical order). - autovj.customMode: 'sequential' rotates one selected kind per step (like 'all'); 'simultaneous' changes every selected kind each step, scenes first so a scene load does not wipe the other changes. - 'all' behaviour unchanged. Locks are respected; if nothing can change the switch reports failure. - Admin: kind checkboxes and step mode segment, TR/EN strings. - MCP sv_set_autovj: source 'custom', customKinds and customMode, validated. - Tests: 6 new (2922 total).
- backgroundLayers() excludes transparent layers (settings.background. transparent or type 'transparent'): the renderer draws the mode before its transparency check, so switching made overlays opaque. If every background layer is locked or transparent the switch reports failure. - sv_set_autovj: Studio preset ids that were already saved in picks.backgrounds and have since been deleted are pruned instead of rejected; new unknown preset ids are still rejected. - EN UI: translate 'Hepsi'; separate the pick count from its label. - Tests: 3 new (2925 total).
- i18n: Colour/colours -> Color/colors in all English strings so the
UI spells it consistently (e.g. 'Color Preset').
- Auto VJ pick box: the count keeps a gap from its label
('Which ones 2 / 43').
feat(autovj): add backgrounds and custom sources
…reo image Refs CaYatur#638 A new visualizer in Particles & Events. It is a fountain: every frequency band launches its own particles. The launch angle comes from where the band sits between the left and right channel, the scatter from how diffuse it is, and the count and height from the band's level. A centred kick draws an upright column, a guitar panned left bends its arcs to the left, a wide reverb opens like a fan. Mono input stands straight up, which is correct. The stereo measurement is new: src/shared/stereo-bands.js runs a band-pass biquad per band over timeL/timeR and keeps sum(L^2), sum(R^2), sum(LR): - pan = (R^2 - L^2) / (R^2 + L^2), exact at hard left, centre, hard right (sin(p*pi/2) under constant-power panning); - spread = 1 - correlation: a single panned source is 0 wherever it sits, decorrelated or anti-phase sound is 1. analysis.js's width gives a hard-panned mono guitar and a wide reverb the same value (1), so it is deliberately not reused here. Silence and 8-bit quantisation noise read as centred. 0.07 ms per frame for 8 bands in Node; no allocation per frame. Levels come from audio.getBars, so every control the catalog flags reveal works: bar count (band count), frequency range, the whole Spectrum Measurement group, line width, amplitude and the secondary colour. The stereo bands use the spectrum's own edges (SVSpectrum.bandEdges), so with a mel or bark scale the level and the pan come from the same band. Drawn with source-over and butt caps: additive blending washed out on the default light background, and round caps cost frames on integrated graphics (Iris Xe, 1080p, uncapped: 118/92 fps with round, 124/118 butt). Checks: - npm test: 2941 pass (16 new); doc counts updated. - tests/stereo-bands.test.js runs the real mode file in a vm with a recording canvas: left/right/mono lean, diffuse sound fans out, each band in its own place, levels from getBars, deterministic output, and every page loads the helper. 10 of 10 mutations caught. - npm run smoke (isolated --user-data-dir): PASS, 60 visualizer modes drawn. - npm run bench, vsync on, 1080p on Iris Xe: 58.2 / 60.0 / 58.4 fps, p95 16.9 ms in three runs (Goniometer 59.4 / 60.0, Bouncing Balls 60.0). - The mode sheet in docs/screenshots is not regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fountain sat at 94% of the canvas, so a full screen left a dead band under the launch line. The floor is now the bottom edge. trailHeight scales those same arcs. 1 keeps today's peak. Up to 3 lets them leave the top of the frame and fall back. The slider sits on the layer and on the classic visualizer card, and the MIDI/OSC target uses the same range.
The sheet is drawn from the catalogue and now holds 60 modes, including Stereo Trails. That mode waits with the other trail builders so its tile is not an empty first frame.
Owner
Author
|
Superseded by CaYatur#732, the same commit opened against the original repository. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs CaYatur#638
What
A new visualizer, Stereo Trails (
stereotrails, Stereo İzler), in Particles & Events. It is a fountain: every frequency band launches its own particles.A centred kick draws an upright column. A guitar panned left bends its arcs to the left. A wide reverb opens like a fan. Mono input stands straight up, which is the correct reading.
Why
CaYatur#638 asks for more original modes. Since CaYatur#566 every frame carries real left and right channels, but only the Goniometer and the Level Meter read them, and both are meters. This turns the stereo image into a visual mode.
How
src/shared/stereo-bands.js(new, pure arithmetic, Node-tested) runs one band-pass biquad per band overtimeL/timeRand keeps ΣL², ΣR² and ΣLR:analysis.js's width is deliberately not reused: it gives a hard-panned mono guitar and a wide reverb the same value (1).The mode (in
generative2.js, where the CaYatur#638 batches live):audio.getBars, so every control the catalog flags reveal does something: bar count (band count), frequency range, the whole Spectrum Measurement group, line width, amplitude and the secondary colour.SVSpectrum.bandEdges, so with a mel or bark scale the level and the pan come from the same band.lighter, because additive blending washed out on the default light background. Butt caps, not round: Iris Xe, 1080p, uncapped bench gave 118 and 92 fps with round caps against 124 and 118 with butt.Wiring:
stereo-bands.jsloaded on all four pages that draw modes (visualizer, overlay, exporter, admin);Checks
npm test: 2941 pass, 16 of them new.tests/stereo-bands.test.js, the measurement: checked against known-answer signals (panned tones, mono, constant-power pan law, anti-phase, decorrelated tones, silence, byte vs float input, mel edges).tests/stereo-bands.test.js, the mode: the real mode file runs in avmwith a recording canvas. The tests check that left, right and mono sound lean the right way, that diffuse sound fans out, that each band sits in its own place, that subtle stereo visibly leans, that levels come fromgetBars, that output is deterministic, and that every page loads the helper.Math.random, missing script tag).npm run smoke: PASS with an isolated--user-data-dir; 60 visualizer modes drawn, and the real profile was left unchanged.npm run bench, vsync on, 1080p, Iris Xe: 58.2 / 60.0 / 58.4 fps with p95 16.9 ms over three runs. In the same runs the Goniometer read 59.4 / 60.0 and Bouncing Balls 60.0.For the reviewer
scripts/bench.jsdescribes). It did not happen with vsync on, but treat the uncapped figures as CPU-side only.docs/screenshots/modes-visualizer.jpgstill pictures 59 modes.tests/copyright-headers.test.jsrequires.🤖 Generated with Claude Code