fix: LFO→cutoff modulation, visual knob feedback, persistent hints
- Fix LFO→Filter cutoff: add scaling Gain nodes so LFO (-1..1) maps to meaningful Hz modulation (±cutoff value). Same fix for LFO→Osc freq. Mod scale updates dynamically when user changes the base param value. - Visual modulation indicator: knobs receiving LFO/modulation show a pulsing dashed ring animation (spin + pulse) around the knob arc - Persist hint usage per level: using a hint permanently caps that level at 2 stars — survives reload/restart. No more cheating by restarting! - Hint state stored in separate localStorage key (synthquest-hints) - Admin reset also clears hint history Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,13 @@ import KeyboardWidget from './KeyboardWidget.jsx';
|
||||
import SequencerWidget from './SequencerWidget.jsx';
|
||||
import PianoRollWidget from './PianoRollWidget.jsx';
|
||||
|
||||
// Map input port names → the param name they modulate (for visual feedback)
|
||||
const PORT_TO_PARAM = {
|
||||
filter: { cutoff: 'frequency' },
|
||||
oscillator: { freq: 'frequency', detune: 'detune' },
|
||||
vca: { cv: 'gain' },
|
||||
};
|
||||
|
||||
export default function ModuleNode({ mod, zoom, onStartConnect, onPortPosition }) {
|
||||
const def = getModuleDef(mod.type);
|
||||
if (!def) return null;
|
||||
@@ -17,6 +24,15 @@ export default function ModuleNode({ mod, zoom, onStartConnect, onPortPosition }
|
||||
// Merge default params
|
||||
const params = { ...Object.fromEntries(Object.entries(def.params).map(([k, v]) => [k, v.default])), ...mod.params };
|
||||
|
||||
// Find which params are being modulated (have an incoming connection on their corresponding port)
|
||||
const modulatedParams = new Set();
|
||||
const portMap = PORT_TO_PARAM[mod.type] || {};
|
||||
for (const conn of state.connections) {
|
||||
if (conn.to.moduleId === mod.id && portMap[conn.to.port]) {
|
||||
modulatedParams.add(portMap[conn.to.port]);
|
||||
}
|
||||
}
|
||||
|
||||
const handleParamChange = useCallback((name, value) => {
|
||||
updateModuleParam(mod.id, name, value);
|
||||
updateParam(mod.id, name, value);
|
||||
@@ -117,6 +133,7 @@ export default function ModuleNode({ mod, zoom, onStartConnect, onPortPosition }
|
||||
max={paramDef.max}
|
||||
onChange={v => handleParamChange(name, v)}
|
||||
color={color}
|
||||
modulated={modulatedParams.has(name)}
|
||||
/>
|
||||
<span className="param-value">
|
||||
{params[name] >= 1000 ? `${(params[name] / 1000).toFixed(1)}k` :
|
||||
|
||||
Reference in New Issue
Block a user