diff --git a/web/sandbox/studio.html b/web/sandbox/studio.html
index 0d19e3b..2fe8f66 100644
--- a/web/sandbox/studio.html
+++ b/web/sandbox/studio.html
@@ -448,96 +448,112 @@ function setupCanvas(canvas, size) {
function drawKnob(ctx, size, norm) {
const cx = size / 2, cy = size / 2 + 1;
- const outerR = size * 0.46; // chrome-bezel outer edge
- const ringW = size * 0.075; // metallic ring width (~4px @ 56)
- const dialR = outerR - ringW; // dark dial radius
- const ringMid = (outerR + dialR) / 2;
+ const totalR = size * 0.46; // outer edge of the whole knob
+ const rimW = size * 0.045; // dark rim thickness (thin)
+ const discR = totalR - rimW; // metallic disc fills most of inside
ctx.clearRect(0, 0, size, size);
- // (1) drop shadow under the whole knob — gives it weight on the panel
+ // (1) drop shadow under the whole knob
ctx.save();
ctx.shadowColor = 'rgba(0,0,0,0.7)';
ctx.shadowBlur = 6;
ctx.shadowOffsetY = 2;
ctx.fillStyle = '#000';
ctx.beginPath();
- ctx.arc(cx, cy, outerR + 0.5, 0, Math.PI * 2);
+ ctx.arc(cx, cy, totalR + 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
- // (2) amber value arc OUTSIDE the bezel (faint track + bright active portion)
+ // (2) amber value arc OUTSIDE everything (faint full track + bright active)
const startA = Math.PI * 0.78, endA = Math.PI * 2.22;
ctx.lineCap = 'round';
ctx.lineWidth = 1.5;
ctx.strokeStyle = 'rgba(232,160,80,0.10)';
ctx.beginPath();
- ctx.arc(cx, cy, outerR + 3, startA, endA);
+ ctx.arc(cx, cy, totalR + 4, startA, endA);
ctx.stroke();
ctx.strokeStyle = '#e8a050';
ctx.shadowBlur = 3;
ctx.shadowColor = 'rgba(232,160,80,0.55)';
ctx.beginPath();
- ctx.arc(cx, cy, outerR + 3, startA, startA + norm * (endA - startA));
+ ctx.arc(cx, cy, totalR + 4, startA, startA + norm * (endA - startA));
ctx.stroke();
ctx.shadowBlur = 0;
- // (3) METALLIC RING — vertical gradient (light top, dark bottom).
- // This is the "chrome bezel" the reference shows.
- const ringGrad = ctx.createLinearGradient(0, cy - outerR, 0, cy + outerR);
- ringGrad.addColorStop(0, '#c4beb2');
- ringGrad.addColorStop(0.30, '#807a72');
- ringGrad.addColorStop(0.55, '#3a3631');
- ringGrad.addColorStop(0.85, '#1c1a17');
- ringGrad.addColorStop(1, '#0a0908');
- ctx.strokeStyle = ringGrad;
- ctx.lineWidth = ringW;
+ // (3) thin DARK RIM around the outside (the body of the knob)
+ const rim = ctx.createLinearGradient(0, cy - totalR, 0, cy + totalR);
+ rim.addColorStop(0, '#2a2826');
+ rim.addColorStop(0.5, '#0a0908');
+ rim.addColorStop(1, '#040404');
+ ctx.strokeStyle = rim;
+ ctx.lineWidth = rimW;
ctx.beginPath();
- ctx.arc(cx, cy, ringMid, 0, Math.PI * 2);
+ ctx.arc(cx, cy, totalR - rimW / 2, 0, Math.PI * 2);
ctx.stroke();
- // (4) thin bright highlight on the very top of the bezel
- ctx.strokeStyle = 'rgba(255,255,255,0.22)';
- ctx.lineWidth = 0.6;
- ctx.beginPath();
- ctx.arc(cx, cy, outerR - 0.3, Math.PI * 1.18, Math.PI * 1.82);
- ctx.stroke();
-
- // (5) inner shadow where the dial sinks below the bezel
- ctx.strokeStyle = 'rgba(0,0,0,0.7)';
- ctx.lineWidth = 0.8;
- ctx.beginPath();
- ctx.arc(cx, cy, dialR + 0.4, 0, Math.PI * 2);
- ctx.stroke();
-
- // (6) dial body — pure charcoal/black, NO warm tint
- const dial = ctx.createRadialGradient(
- cx - dialR * 0.35, cy - dialR * 0.5, 0,
- cx, cy, dialR * 1.1
+ // (4) BIG METALLIC DISC — fills most of the inside (the "circulito metalico
+ // que ocupa casi todo"). Radial gradient, brushed-metal feel.
+ const disc = ctx.createRadialGradient(
+ cx, cy - discR * 0.25, 0,
+ cx, cy + discR * 0.4, discR * 1.4
);
- dial.addColorStop(0, '#2a2a28');
- dial.addColorStop(0.5, '#141312');
- dial.addColorStop(1, '#040404');
- ctx.fillStyle = dial;
+ disc.addColorStop(0, '#b6b0a6'); // lit upper-center
+ disc.addColorStop(0.45, '#8a847a');
+ disc.addColorStop(0.85, '#46423d');
+ disc.addColorStop(1, '#2a2722'); // bottom dark
+ ctx.fillStyle = disc;
ctx.beginPath();
- ctx.arc(cx, cy, dialR, 0, Math.PI * 2);
+ ctx.arc(cx, cy, discR, 0, Math.PI * 2);
ctx.fill();
- // (7) faint glossy crescent on the dial's upper half
- ctx.strokeStyle = 'rgba(255,255,255,0.07)';
+ // (5) brushed-metal radial striations (very subtle, deterministic)
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(cx, cy, discR - 0.5, 0, Math.PI * 2);
+ ctx.clip();
+ for (let i = 0; i < 96; i++) {
+ const a = (i / 96) * Math.PI * 2;
+ const alpha = 0.025 + ((i * 9301 + 49297) % 233) / 233 * 0.04;
+ ctx.strokeStyle = `rgba(255, 240, 220, ${alpha.toFixed(3)})`;
+ ctx.lineWidth = 0.5;
+ ctx.beginPath();
+ ctx.moveTo(cx, cy);
+ ctx.lineTo(cx + Math.cos(a) * discR, cy + Math.sin(a) * discR);
+ ctx.stroke();
+ }
+ ctx.restore();
+
+ // (6) thin shadow line where disc meets rim (depth)
+ ctx.strokeStyle = 'rgba(0,0,0,0.55)';
ctx.lineWidth = 0.7;
ctx.beginPath();
- ctx.arc(cx, cy, dialR - 1, Math.PI * 1.18, Math.PI * 1.82);
+ ctx.arc(cx, cy, discR + 0.4, 0, Math.PI * 2);
ctx.stroke();
- // (8) indicator tick — cream, from mid to dial edge
- const ang = startA + norm * (endA - startA);
- ctx.strokeStyle = 'rgba(245,235,215,0.92)';
- ctx.lineWidth = 1.7;
- ctx.lineCap = 'round';
+ // (7) bright highlight crescent on the top edge of the disc
+ ctx.strokeStyle = 'rgba(255,255,255,0.30)';
+ ctx.lineWidth = 0.8;
ctx.beginPath();
- ctx.moveTo(cx + Math.cos(ang) * dialR * 0.5, cy + Math.sin(ang) * dialR * 0.5);
- ctx.lineTo(cx + Math.cos(ang) * dialR * 0.88, cy + Math.sin(ang) * dialR * 0.88);
+ ctx.arc(cx, cy, discR - 0.5, Math.PI * 1.18, Math.PI * 1.82);
ctx.stroke();
+
+ // (8) small DARK NOTCH indicator on the disc, rotates with value
+ const ang = startA + norm * (endA - startA);
+ const tipR = discR * 0.86;
+ const baseR = discR * 0.55;
+ const perp = ang + Math.PI / 2;
+ const half = size * 0.028;
+ const tipX = cx + Math.cos(ang) * tipR;
+ const tipY = cy + Math.sin(ang) * tipR;
+ const bX = cx + Math.cos(ang) * baseR;
+ const bY = cy + Math.sin(ang) * baseR;
+ ctx.fillStyle = 'rgba(8,7,6,0.95)';
+ ctx.beginPath();
+ ctx.moveTo(tipX, tipY);
+ ctx.lineTo(bX + Math.cos(perp) * half, bY + Math.sin(perp) * half);
+ ctx.lineTo(bX - Math.cos(perp) * half, bY - Math.sin(perp) * half);
+ ctx.closePath();
+ ctx.fill();
}
document.querySelectorAll('canvas.hw-knob').forEach((canvas) => {