Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

fix(perc): drum pan by QWERTY physical key position, not kit geometry

The old drumPanFor() mixed grid-offset bias (-0.32 / +0.32) with
kit-geometry drum offsets (kick center, ride right, crash left, etc).
That worked on paper but mismatched the physical gesture — your hand
is at a specific keyboard column, but the pan was being computed from
the drum type assigned to that key.

New model: KEY_QWERTY_PAN maps each drum key to pan -0.9..+0.9 based
on its column index on a US QWERTY keyboard. Left-side keys (q/a)
pan hard left, right-side keys (p/;) pan hard right, center keys
(t/g/b) stay near center. Computed as (col - 4.5) / 4.5 * 0.9 across
the three rows:

Row 0 (qwerty): q=-0.9 w=-0.7 e=-0.5 r=-0.3 t=-0.1 y=+0.1 u=+0.3 i=+0.5 o=+0.7 p=+0.9
Row 1 (asdf): a=-0.9 s=-0.7 d=-0.5 f=-0.3 g=-0.1 h=+0.1 j=+0.3 k=+0.5 l=+0.7
Row 2 (zxcv): c=-0.5 v=-0.3 b=-0.1 n=+0.1 m=+0.3

drumPanFor(letter, gridOffset, key) now checks KEY_QWERTY_PAN[key]
first and falls back to grid-side bias if no key provided (reverse
playback replay path). All 3 live trigger sites updated to pass the
key: keyboard act() drum branch, touch-tap drum pad, drag-rollover.

Result: your fingers and the stereo field line up directly — no
mental translation needed between "which drum is this" and "where
does it pan".

+36 -38
+36 -38
fedac/native/pieces/notepat.mjs
··· 551 551 return null; 552 552 } 553 553 554 - // === DRUM PAN GEOMETRY === 555 - // Tone pan is pitch-based (low notes left, high notes right) which kind of 556 - // coincidentally matches the QWERTY position because notepat's key mapping 557 - // puts low naturals on the left. For DRUMS that model is wrong — a kick 558 - // shouldn't pan hard left just because it's on the C key. Instead drums pan 559 - // by physical kit geometry (from drummer's perspective / standard mix): 560 - // - kicks + snares near center 561 - // - hats slightly right (drummer's right hand) 562 - // - ride right, crash + splash left 563 - // - perc accents spread for color 564 - // Then the grid side biases the whole hit — left grid → left-of-center, 565 - // right grid → right-of-center — so the two grids occupy distinct stereo 566 - // spaces when both have percussion on. 567 - const DRUM_PAN_OFFSET = { 568 - c: 0.00, // kick — center 569 - d: -0.05, // snare — barely off center 570 - e: 0.10, // clap — slightly right 571 - f: -0.10, // snap — slightly left 572 - g: 0.25, // closed hi-hat — drummer's right 573 - a: 0.32, // open hi-hat — drummer's right 574 - b: 0.45, // ride — far right (drummer's right side) 575 - "c#": -0.45, // crash — drummer's left 576 - "d#": -0.55, // splash — far left 577 - "f#": 0.22, // cowbell — right 578 - "g#": -0.18, // wood block — left 579 - "a#": 0.30, // tambourine — right 554 + // === DRUM PAN: QWERTY physical key position === 555 + // Pan each drum hit by where the key actually lives on the keyboard so 556 + // your fingers and the stereo field line up. Left-side keys (q/a/z/c) 557 + // pan hard left, right-side keys (p/;/m) pan hard right, middle keys 558 + // (t/g/b) stay centered. This beats kit-geometry pan (kick-center, 559 + // ride-right, crash-left) because it matches the physical gesture of 560 + // playing — you don't have to think about "which drum is this", the 561 + // pan just follows your hand. 562 + // 563 + // Values computed from QWERTY column (col - 4.5) / 4.5 * 0.9 across 564 + // three rows. Only drum keys included (c/d/e/f/g/a/b naturals + sharps 565 + // on both grids). Non-drum keys fall through to the fallback below. 566 + const KEY_QWERTY_PAN = { 567 + // Row 0 (qwerty top row): q w e r t y u i o p 568 + q: -0.9, w: -0.7, e: -0.5, r: -0.3, t: -0.1, 569 + y: 0.1, u: 0.3, i: 0.5, o: 0.7, p: 0.9, 570 + // Row 1 (asdf home row): a s d f g h j k l 571 + a: -0.9, s: -0.7, d: -0.5, f: -0.3, g: -0.1, 572 + h: 0.1, j: 0.3, k: 0.5, l: 0.7, 573 + // Row 2 (zxcv bottom row): c v b n m 574 + c: -0.5, v: -0.3, b: -0.1, n: 0.1, m: 0.3, 580 575 }; 581 576 582 - function drumPanFor(letter, gridOffset) { 583 - // gridBias: left grid skews left, right grid skews right. Keeps both grids 584 - // audible simultaneously without collapsing into a single phantom center. 577 + function drumPanFor(letter, gridOffset, key) { 578 + // Primary: QWERTY physical column position. This is what the user sees 579 + // and feels — the pan matches where their hand is. 580 + if (key && KEY_QWERTY_PAN[key] !== undefined) { 581 + return KEY_QWERTY_PAN[key]; 582 + } 583 + // Fallback: grid-side bias (used by reverse playback where we don't 584 + // have the original key, only the letter + gridOffset). 585 585 const gridBias = gridOffset === 1 ? 0.32 : -0.32; 586 - const drumOffset = DRUM_PAN_OFFSET[letter] ?? 0; 587 - // Drum offset at 0.7 strength so kit geometry shows but grid side dominates. 588 - return Math.max(-0.9, Math.min(0.9, gridBias + drumOffset * 0.7)); 586 + return Math.max(-0.9, Math.min(0.9, gridBias)); 589 587 } 590 588 591 589 // Push a drum flash entry so paint() will pulse the background in that ··· 1555 1553 const drumVol = (1.0 + velocity * 0.8) * master; 1556 1554 const drumPitch = Math.pow(2, effectivePitchShift()); 1557 1555 // Drums get their own pan (kit geometry + grid bias), not the 1558 - // pitch-based tone pan calculated above. 1559 - const drumPan = drumPanFor(letter, offset); 1556 + // QWERTY physical key position → pan. Left keys pan left, right pan right. 1557 + const drumPan = drumPanFor(letter, offset, key); 1560 1558 activeDrumKeys[key] = triggerPercussionDown( 1561 1559 sound, letter, noteOctave, drumVol, drumPan, drumPitch, key, drumName 1562 1560 ); ··· 1881 1879 const touchDrum = percussionDrumFor(hitNote.letter, hitNote.gridOffset); 1882 1880 if (touchDrum) { 1883 1881 const touchDrumPitch = Math.pow(2, effectivePitchShift()); 1884 - // Drums pan by kit geometry + grid bias, not pitch-based tone pan. 1885 - const touchDrumPan = drumPanFor(hitNote.letter, hitNote.gridOffset); 1882 + // QWERTY physical key position → pan (matches where finger lands). 1883 + const touchDrumPan = drumPanFor(hitNote.letter, hitNote.gridOffset, hitNote.key); 1886 1884 const drumHold = triggerPercussionDown( 1887 1885 sound, hitNote.letter, hitNote.octave, 1.8, touchDrumPan, touchDrumPitch, hitNote.key, touchDrum 1888 1886 ); ··· 2020 2018 const rollDrum = percussionDrumFor(hitNote.letter, hitNote.gridOffset); 2021 2019 if (rollDrum) { 2022 2020 const rollDrumPitch = Math.pow(2, effectivePitchShift()); 2023 - // Drums pan by kit geometry + grid bias, not pitch-based tone pan. 2024 - const rollDrumPan = drumPanFor(hitNote.letter, hitNote.gridOffset); 2021 + // QWERTY physical key position → pan (matches where finger lands). 2022 + const rollDrumPan = drumPanFor(hitNote.letter, hitNote.gridOffset, hitNote.key); 2025 2023 const drumHold = triggerPercussionDown( 2026 2024 sound, hitNote.letter, hitNote.octave, 1.8, rollDrumPan, rollDrumPitch, hitNote.key, rollDrum 2027 2025 );