Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

notepat: volume/drive pointer events + smooth drift reversal

Two bugs in the seismic-nightjar-beacon flash:

1. Volume + drive sliders didn't respond to mouse/touch.
The drag-init loop in act() iterated over a hardcoded list
`["fx", "echo", "pitch", "crush"]` so the new "volume" and "drive"
row IDs never entered the hit-test, even though the row state
(fxRows.volume / fxRows.drive) and the dragging-flag setters were
already in place. Added both names to the loop.

2. Spacebar didn't smoothly reverse the wave drift — it froze.
Previous logic grew the offset at exactly 1× real-time (dt per frame)
while spaceHeld, which keeps cursor_pos = write_pos - offset
constant in absolute time. The displayed left half therefore stayed
FROZEN instead of drifting RIGHT.

Replaced with a velocity model:
waveDriftSpeed lerps toward +1 (live, wave drifts left) or -1
(replay, wave drifts right) with a 6-frame time constant.
d_offset/dt = 1 - waveDriftSpeed, so:
speed = +1 → offset doesn't change → wave drifts LEFT (live)
speed = 0 → offset grows at 1× → wave frozen (transition mid-point)
speed = -1 → offset grows at 2× → cursor retreats at 1× → wave
drifts RIGHT
Pressing space now produces a continuous deceleration of the left
drift, a momentary stop, and acceleration into a right drift —
matching what the user described as "smooth direction reversal".
Releasing space inverts the lerp, smoothly catching back up to live.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+29 -12
+29 -12
fedac/native/pieces/notepat.mjs
··· 140 140 // Waveform-strip view cursor — how many seconds in the past the playhead 141 141 // sits relative to the live audio edge. 0 = live (wave drifts LEFT as 142 142 // real time advances). Grows when spacebar is held (wave drifts RIGHT, 143 - // backwards-replay scrub). Shrinks back to 0 on release so the display 144 - // catches up to live audio over ~0.5 s. 143 + // backwards-replay scrub). Shrinks back to 0 on release. 145 144 let waveViewOffsetSec = 0; 146 145 // Max retreat — also caps how much of the right half can fill in with 147 146 // post-cursor audio. Matches recordStripSeconds / 2 so the right half 148 147 // fully paints when the cursor has retreated half the visible window. 149 148 const WAVE_VIEW_MAX_OFFSET_SEC = 2.0; 149 + // Smoothed "drift speed" of the displayed time-range. 150 + // = +1.0 → display advances at 1× real-time → wave drifts LEFT (live) 151 + // = 0.0 → display frozen → wave doesn't drift 152 + // = -1.0 → display retreats at 1× real-time → wave drifts RIGHT (replay) 153 + // On spacebar press/release we smoothly lerp this between +1 and -1 so 154 + // the drift DECELERATES, momentarily stops, then REVERSES — instead of 155 + // snapping. The relationship to view offset: 156 + // d_offset/dt = 1.0 - waveDriftSpeed 157 + let waveDriftSpeed = 1.0; 150 158 151 159 // Pitch shift — assignable to either trackpad axis 152 160 let pitchShift = 0; // -1 to +1, 0 = no shift ··· 2877 2885 // FX rows: sliders + per-effect X/Y trackpad assignment toggles 2878 2886 { 2879 2887 const fxRows = globalThis.__fxRows || {}; 2880 - for (const rowId of ["fx", "echo", "pitch", "crush"]) { 2888 + for (const rowId of ["fx", "echo", "pitch", "crush", "volume", "drive"]) { 2881 2889 const row = fxRows[rowId]; 2882 2890 if (!row) continue; 2883 2891 if (pointInRect(x, y, row.xBox)) { ··· 5658 5666 stopSampleRecording(sound, "max-duration"); 5659 5667 } 5660 5668 5661 - // Advance / retreat the waveform-strip view cursor based on spacebar. 5662 - // 1x audio-rate retreat on press → the wave drifts RIGHT at a natural 5663 - // speed. 2x catch-up on release → wave snaps forward noticeably faster 5664 - // than normal drift so the eye can tell the cursor is "coming back". 5669 + // Smoothly lerp waveDriftSpeed toward target (+1 live, -1 replay) so 5670 + // the visible wave decelerates → stops → reverses → accelerates 5671 + // instead of snapping. Then derive offset growth rate from drift 5672 + // speed (d_offset/dt = 1 - drift_speed). Cap offset at the configured 5673 + // max; if we hit the cap, force drift_speed back to +1 so we don't 5674 + // accumulate further (cursor settles at the cap and resumes live drift 5675 + // at the floor of the visible window). 5665 5676 const dtSec = 1 / 60; 5666 - if (spaceHeld) { 5667 - waveViewOffsetSec = Math.min(WAVE_VIEW_MAX_OFFSET_SEC, 5668 - waveViewOffsetSec + dtSec); 5669 - } else if (waveViewOffsetSec > 0) { 5670 - waveViewOffsetSec = Math.max(0, waveViewOffsetSec - dtSec * 2); 5677 + const driftTarget = spaceHeld ? -1.0 : 1.0; 5678 + // 0.10 ≈ 6-frame time constant — visibly smooth but quick to engage. 5679 + waveDriftSpeed += (driftTarget - waveDriftSpeed) * 0.10; 5680 + const dOffsetDt = 1.0 - waveDriftSpeed; 5681 + waveViewOffsetSec += dOffsetDt * dtSec; 5682 + if (waveViewOffsetSec < 0) waveViewOffsetSec = 0; 5683 + if (waveViewOffsetSec > WAVE_VIEW_MAX_OFFSET_SEC) { 5684 + waveViewOffsetSec = WAVE_VIEW_MAX_OFFSET_SEC; 5685 + // Clamp the drift back to +1 (live) so subsequent frames don't 5686 + // pile up offset that we'd then need to unwind on release. 5687 + if (waveDriftSpeed < 1.0) waveDriftSpeed = 1.0; 5671 5688 } 5672 5689 // Update dark/light mode via global theme (every ~5 seconds) 5673 5690 if (frame % 300 === 0) {