Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

notepat: scrolling record-needle waveform strip above pads

Classic DJ-turntable-style horizontal waveform sitting just above the
pad grids. 4-second history of the mixed speaker output, downsampled
to one peak value per pixel column. Refreshed every 4 frames for
cheap paint cost, uses the native C `sound.speaker.getRecentBuffer()`
that was already wired up for spacebar reverse-play.

- Background rectangle + center zero line
- Per-column vertical peak segments (centered on mid-line)
- Color gradient: cold blue at low amplitude → amber → warm red hot
- Red "record needle" at the right edge marking the current write
position (what gets snapshot on spacebar press)

Existing per-lane visualizer bars are shortened to end 2px above the
new strip so it fits without overlapping. Lets you see at a glance
what material a reverse-loop press is about to snapshot.

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

+56 -1
+56 -1
fedac/native/pieces/notepat.mjs
··· 4110 4110 const leftX = margin; 4111 4111 const rightX = w - gridW - margin; 4112 4112 4113 + // Scrolling record-needle strip: the last ~4 seconds of mixed speaker 4114 + // output, always rolling regardless of whether notes are active. Think 4115 + // classic DJ turntable display — you can see the waveform the spacebar 4116 + // reverse-play would snap back into. Refreshed every 4 frames to keep 4117 + // paint cheap; downsampled to one peak value per pixel column. 4118 + const recordStripH = 22; 4119 + const recordStripSeconds = 4; 4120 + const recordStripTop = Math.max(topBarH + 1, gridTop - recordStripH - 2); 4121 + const recordStripBottom = recordStripTop + recordStripH; 4122 + if (frame % 4 === 0 && sound?.speaker?.getRecentBuffer) { 4123 + const snap = sound.speaker.getRecentBuffer(recordStripSeconds); 4124 + if (snap && snap.data && snap.data.length > 0) { 4125 + globalThis.__recordStripData = snap.data; 4126 + } 4127 + } 4128 + const rsData = globalThis.__recordStripData; 4129 + if (rsData && rsData.length > 4) { 4130 + const rsX = margin; 4131 + const rsW = w - margin * 2; 4132 + const midY = Math.floor((recordStripTop + recordStripBottom) / 2); 4133 + // Background strip 4134 + ink(dark ? 20 : 235, dark ? 15 : 225, dark ? 30 : 210, 160); 4135 + box(rsX, recordStripTop, rsW, recordStripH, true); 4136 + // Center zero-line 4137 + ink(dark ? 80 : 140, dark ? 80 : 140, dark ? 90 : 150, 120); 4138 + line(rsX, midY, rsX + rsW, midY); 4139 + // Per-pixel-column peak of that time-slice. 4140 + const samplesPerCol = rsData.length / rsW; 4141 + const amp = Math.floor(recordStripH * 0.45); 4142 + for (let x = 0; x < rsW; x++) { 4143 + const i0 = Math.floor(x * samplesPerCol); 4144 + const i1 = Math.min(rsData.length, Math.floor((x + 1) * samplesPerCol)); 4145 + let peak = 0; 4146 + for (let i = i0; i < i1; i++) { 4147 + const a = Math.abs(rsData[i]); 4148 + if (a > peak) peak = a; 4149 + } 4150 + // Clip extreme outliers so a transient hot sample doesn't dominate the 4151 + // column-height math and flatten everything else visually. 4152 + if (peak > 1.0) peak = 1.0; 4153 + const h = Math.max(1, Math.round(peak * amp)); 4154 + // Color fades from warm (loud) through amber (mid) to cold (quiet). 4155 + const r = Math.min(255, Math.round(120 + peak * 140)); 4156 + const g = Math.round(120 + peak * 80); 4157 + const b = Math.round(90 + (1 - peak) * 120); 4158 + ink(r, g, b, 220); 4159 + line(rsX + x, midY - h, rsX + x, midY + h); 4160 + } 4161 + // Right-edge "record needle" — where new samples are being written. 4162 + ink(240, 80, 80, 220); 4163 + line(rsX + rsW - 1, recordStripTop, rsX + rsW - 1, recordStripBottom); 4164 + } 4165 + 4113 4166 // Waveform visualizer bars only in lanes above pad grids (not full-screen). 4167 + // Shortened so the record-needle strip fits above the pads without 4168 + // clipping the existing per-lane visualizer. 4114 4169 const wf = sound?.speaker?.waveforms?.left; 4115 4170 if (wf && activeCount > 0) { 4116 4171 const wfTop = topBarH + 1; 4117 - const wfBottom = Math.max(wfTop + 6, gridTop - 2); 4172 + const wfBottom = Math.max(wfTop + 6, recordStripTop - 2); 4118 4173 const wfH = wfBottom - wfTop; 4119 4174 if (wfH > 2) { 4120 4175 const wfBottomY = wfTop + wfH;