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:
Jose Luis
2026-03-21 02:44:28 +01:00
parent c4a2cb3cef
commit f0100eb64f
7 changed files with 120 additions and 15 deletions

View File

@@ -19,7 +19,7 @@ function describeArc(cx, cy, r, startDeg, endDeg) {
return `M ${start.x} ${start.y} A ${r} ${r} 0 ${large} 0 ${end.x} ${end.y}`;
}
export default function Knob({ value, min, max, onChange, color = 'var(--accent)', label, unit, formatValue }) {
export default function Knob({ value, min, max, onChange, color = 'var(--accent)', label, unit, formatValue, modulated = false }) {
const ref = useRef(null);
const dragRef = useRef(null);
const inputRef = useRef(null);
@@ -122,9 +122,13 @@ export default function Knob({ value, min, max, onChange, color = 'var(--accent)
}
return (
<div className="knob-container" onWheel={handleWheel} onDoubleClick={handleDoubleClick}>
<div className={`knob-container ${modulated ? 'knob-modulated' : ''}`} onWheel={handleWheel} onDoubleClick={handleDoubleClick}>
<svg className="knob-svg" viewBox={`0 0 ${SIZE} ${SIZE}`}
onPointerDown={handlePointerDown} ref={ref}>
{/* Modulation glow ring */}
{modulated && (
<circle className="knob-mod-ring" cx={cx} cy={cy} r={RADIUS + 1} style={{ stroke: color }} />
)}
<path className="knob-track" d={trackPath} />
{fillPath && <path className="knob-fill" d={fillPath} style={{ stroke: color }} />}
<circle className="knob-dot" cx={dotPos.x} cy={dotPos.y} r={2} />