As-built: arcade buttons, use-driven control layout, rear I/O, compact case
Reworked the as-built panel around how it's actually used:
- Buttons: replaced the tactile/keycap ("rubber key") buttons with glossy
arcade pushbuttons, colour-keyed — cyan PREV/NEXT, a big green/red PLAY,
amber TAP.
- Layout for use: the rotary encoder sits BELOW the screen (so turning it
never hides the readout) with the buttons spread edge-to-edge underneath —
PREV far left, NEXT far right, a bigger central PLAY (+ TAP) — so you're
far less likely to hit the wrong one mid-performance.
- Rear I/O: external trigger in (footswitch), a 1/4" instrument pass-through
with the click injected, and a shared 1/4" balanced-TRS main out; plus the
monitor speaker and USB-C, each a labelled jack with a tooltip.
- Size: shrank the case to hug its content (max-width 560→380, trimmed
padding and inter-section margins) — the dead margin is gone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4340f1838e
commit
4f76783af4
1 changed files with 61 additions and 41 deletions
|
|
@ -14,8 +14,12 @@
|
|||
• a 4×16 WS2812 ("NeoPixel") RGB matrix (PIO-driven): the bottom row is the
|
||||
beat (cyan downbeats / amber group-starts), and the three rows above stack
|
||||
the current beat's subdivisions as they pass (driven by the finest lane);
|
||||
• an EC11 rotary encoder (turn it: wheel or drag) for tempo, tactile buttons,
|
||||
a MAX98357A-style speaker, USB-C and a PWR LED in a matte 3D-printed case.
|
||||
• controls arranged for use: an EC11 encoder below the screen (turning it
|
||||
never hides the readout) with arcade pushbuttons spread below — PREV far
|
||||
left, NEXT far right, a big central PLAY — so you don't hit the wrong one;
|
||||
• rear I/O: external trigger in (footswitch), a 1/4" instrument pass-through
|
||||
with the click injected, and a shared 1/4" balanced-TRS main out; plus a
|
||||
MAX98357A-style monitor speaker, USB-C and a PWR LED, in a compact matte case.
|
||||
Compare with the idealized /player.html. One file, no deps; shares src/engine.js.
|
||||
-->
|
||||
<script>
|
||||
|
|
@ -43,13 +47,13 @@
|
|||
--panel-bg:#ffffff; --panel-bd:#d2dae4; --field-bg:#f1f4f8; --field-bd:#cdd6e0;
|
||||
}
|
||||
body{
|
||||
margin:0; min-height:100vh; padding:28px 16px 48px;
|
||||
margin:0; min-height:100vh; padding:22px 12px 40px;
|
||||
background:radial-gradient(circle at 50% -8%, var(--bg1), var(--bg2));
|
||||
color:var(--txt);
|
||||
display:flex; flex-direction:column; align-items:center; gap:20px;
|
||||
display:flex; flex-direction:column; align-items:center; gap:14px;
|
||||
}
|
||||
a{color:var(--link)}
|
||||
.topbar{width:100%; max-width:560px; display:flex; align-items:center; justify-content:space-between; gap:10px; font-size:13px; color:var(--muted); flex-wrap:wrap}
|
||||
.topbar{width:100%; max-width:380px; display:flex; align-items:center; justify-content:space-between; gap:10px; font-size:13px; color:var(--muted); flex-wrap:wrap}
|
||||
.topbar b{color:var(--txt)}
|
||||
.topbar-right{ display:flex; align-items:center; gap:12px }
|
||||
.tbtn{ background:transparent; color:var(--muted); border:1px solid var(--panel-bd); border-radius:8px;
|
||||
|
|
@ -58,11 +62,11 @@
|
|||
|
||||
/* ---- the device: matte 3D-printed case ---- */
|
||||
.device{
|
||||
width:100%; max-width:560px; position:relative;
|
||||
width:100%; max-width:380px; position:relative;
|
||||
background:
|
||||
repeating-linear-gradient(115deg, rgba(255,255,255,.012) 0 2px, transparent 2px 4px),
|
||||
linear-gradient(180deg, var(--case), var(--case2));
|
||||
border:1px solid var(--device-bd); border-radius:18px; padding:26px 24px 22px;
|
||||
border:1px solid var(--device-bd); border-radius:18px; padding:16px 14px 14px;
|
||||
box-shadow:0 24px 55px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.05), inset 0 -2px 10px rgba(0,0,0,.6);
|
||||
}
|
||||
.device::before, .device::after, .device .screw{ content:""; position:absolute; width:10px; height:10px; border-radius:50%;
|
||||
|
|
@ -70,7 +74,7 @@
|
|||
.device::before{ top:12px; left:12px } .device::after{ top:12px; right:12px }
|
||||
.screw.bl{ bottom:12px; left:12px } .screw.br{ bottom:12px; right:12px }
|
||||
|
||||
.brandrow{ display:flex; align-items:flex-end; justify-content:space-between; margin:0 4px 16px; }
|
||||
.brandrow{ display:flex; align-items:flex-end; justify-content:space-between; margin:0 2px 12px; }
|
||||
.silk{ color:var(--silk); letter-spacing:.04em }
|
||||
.silk .vk{ font-weight:800; letter-spacing:.16em; font-size:15px; color:var(--silk) }
|
||||
.silk .model{ font-size:10px; text-transform:uppercase; letter-spacing:.18em; opacity:.8 }
|
||||
|
|
@ -86,45 +90,55 @@
|
|||
.tft-cap{ text-align:center; font-size:10px; color:var(--muted); margin-top:7px; letter-spacing:.02em }
|
||||
|
||||
/* ---- 4×16 WS2812 RGB matrix: bottom row = beat, 3 rows above = subdivisions ---- */
|
||||
.ledgrid{ display:flex; flex-direction:column; gap:5px; width:max-content; margin:16px auto 4px;
|
||||
.ledgrid{ display:flex; flex-direction:column; gap:4px; width:max-content; margin:12px auto 3px;
|
||||
background:linear-gradient(180deg,#10221c,var(--pcb)); border:1px solid #07140f; border-radius:5px; padding:8px 9px;
|
||||
box-shadow:inset 0 1px 2px rgba(0,0,0,.6) }
|
||||
.ledrow{ display:flex; gap:5px }
|
||||
.ledrow{ display:flex; gap:4px }
|
||||
.ledrow.beatrow{ margin-top:4px; padding-top:6px; border-top:1px solid rgba(255,255,255,.07) }
|
||||
.npx{ width:15px; height:15px; border-radius:3px; background:#0c0e10; border:1px solid #060708;
|
||||
position:relative; transition:background .05s, box-shadow .05s }
|
||||
.npx::after{ content:""; position:absolute; inset:4px; border-radius:1px; background:rgba(255,255,255,.05) } /* the 5050 die */
|
||||
.ledbar-cap{ text-align:center; font-size:10px; color:var(--muted); margin:2px 0 0; letter-spacing:.02em }
|
||||
|
||||
/* ---- controls: encoder + tactile buttons ---- */
|
||||
.controls{ display:flex; align-items:center; justify-content:center; gap:14px; margin:16px 4px 4px; flex-wrap:wrap }
|
||||
.keys{ display:flex; gap:9px; flex-wrap:wrap; justify-content:center }
|
||||
.key{ display:flex; flex-direction:column; align-items:center; gap:4px }
|
||||
.cap{ width:46px; height:40px; border-radius:7px; background:linear-gradient(180deg,#2c333d,#1a1f27);
|
||||
border:1px solid #3a424e; color:#d4dbe4; font-size:15px; cursor:pointer; display:flex; align-items:center; justify-content:center;
|
||||
box-shadow:0 3px 0 #0b0e12, inset 0 1px 0 rgba(255,255,255,.07); user-select:none; transition:transform .04s, box-shadow .04s }
|
||||
.cap:active{ transform:translateY(2px); box-shadow:0 1px 0 #0b0e12, inset 0 1px 0 rgba(255,255,255,.07) }
|
||||
.cap.play{ background:linear-gradient(180deg,#1f7a4d,#155f3b); border-color:#2e7d32; color:#eafff3 }
|
||||
.cap.play.on{ background:linear-gradient(180deg,#b23b3b,#8f2d2d); border-color:#c0392b }
|
||||
.key small{ font-size:8px; color:var(--silk); letter-spacing:.1em; text-transform:uppercase; opacity:.8 }
|
||||
.enc-wrap{ display:flex; flex-direction:column; align-items:center; gap:4px }
|
||||
.enc{ width:54px; height:54px; border-radius:50%; cursor:ns-resize; position:relative; touch-action:none;
|
||||
/* ---- controls: encoder above (under the screen), arcade buttons spread below ----
|
||||
wheel never hides the readout · PREV far-left / NEXT far-right · big central PLAY */
|
||||
.controls{ display:flex; flex-direction:column; align-items:center; gap:13px; margin:14px 0 2px }
|
||||
.enc-wrap{ display:flex; flex-direction:column; align-items:center; gap:5px }
|
||||
.enc{ width:52px; height:52px; border-radius:50%; cursor:ns-resize; position:relative; touch-action:none;
|
||||
background:repeating-conic-gradient(from 0deg, #424b57 0 7deg, #2c333d 7deg 14deg);
|
||||
border:2px solid #565f6c; box-shadow:0 3px 8px rgba(0,0,0,.5), inset 0 1px 1px rgba(255,255,255,.12) }
|
||||
.enc::before{ content:""; position:absolute; inset:9px; border-radius:50%; background:radial-gradient(circle at 38% 32%,#3b434f,#181c22 75%) }
|
||||
.enc::after{ content:""; position:absolute; left:50%; top:7px; width:3px; height:13px; background:var(--cyan); border-radius:2px;
|
||||
transform-origin:50% 20px; transform:translateX(-50%) rotate(var(--a,0deg)); box-shadow:0 0 5px var(--cyan) }
|
||||
.enc-wrap small{ font-size:8px; color:var(--silk); letter-spacing:.12em; opacity:.8 }
|
||||
.enc::after{ content:""; position:absolute; left:50%; top:7px; width:3px; height:12px; background:var(--cyan); border-radius:2px;
|
||||
transform-origin:50% 19px; transform:translateX(-50%) rotate(var(--a,0deg)); box-shadow:0 0 5px var(--cyan) }
|
||||
.enc-wrap small{ font-size:8px; color:var(--silk); letter-spacing:.12em; opacity:.85 }
|
||||
.keys{ display:flex; align-items:flex-end; justify-content:space-between; width:100%; padding:0 2px }
|
||||
.key-mid{ display:flex; align-items:flex-end; gap:20px }
|
||||
.key{ display:flex; flex-direction:column; align-items:center; gap:6px }
|
||||
.abtn{ width:50px; height:50px; border-radius:50%; border:0; padding:0; cursor:pointer; position:relative; color:#fff; font-size:18px; line-height:1;
|
||||
background:radial-gradient(circle at 36% 30%, rgba(255,255,255,.6), rgba(255,255,255,0) 42%), radial-gradient(circle at 50% 64%, var(--c1,#33d0ff), var(--c2,#0a7fb0));
|
||||
box-shadow:0 0 0 3px #0b0d11, 0 0 0 4px #363c46, 0 5px 7px rgba(0,0,0,.5), inset 0 -3px 6px rgba(0,0,0,.35), inset 0 2px 4px rgba(255,255,255,.28);
|
||||
text-shadow:0 1px 2px rgba(0,0,0,.45); user-select:none; transition:transform .05s, box-shadow .05s, filter .05s }
|
||||
.abtn:active{ transform:translateY(2px); filter:brightness(.92);
|
||||
box-shadow:0 0 0 3px #0b0d11, 0 0 0 4px #363c46, 0 2px 3px rgba(0,0,0,.5), inset 0 -2px 4px rgba(0,0,0,.4), inset 0 2px 4px rgba(255,255,255,.2) }
|
||||
.abtn.nav{ --c1:#33d0ff; --c2:#0a7fb0 }
|
||||
.abtn.tap{ --c1:#ffd56a; --c2:#c98a1f; color:#3a2a00; text-shadow:0 1px 1px rgba(255,255,255,.35); font-size:12px; font-weight:800; letter-spacing:.04em }
|
||||
.abtn.play{ --c1:#4ce08e; --c2:#178f49; width:66px; height:66px; font-size:26px }
|
||||
.abtn.play.on{ --c1:#ff6a6a; --c2:#a82828 }
|
||||
.key small{ font-size:8px; color:var(--silk); letter-spacing:.1em; text-transform:uppercase; opacity:.85 }
|
||||
|
||||
/* ---- speaker grille + ports ---- */
|
||||
.footrow{ display:flex; align-items:center; justify-content:space-between; margin:18px 6px 2px }
|
||||
.grille{ flex:1; height:12px; margin-right:12px; border-radius:5px;
|
||||
/* ---- monitor speaker + rear I/O (1/4" jacks + USB-C) ---- */
|
||||
.grille{ height:11px; margin:13px 8px 9px; border-radius:5px;
|
||||
background:radial-gradient(circle, #000 1.1px, transparent 1.4px) 0 0/8px 8px; opacity:.5 }
|
||||
.usbc{ font-size:8px; color:var(--silk); letter-spacing:.12em; opacity:.7; display:flex; align-items:center; gap:5px }
|
||||
.usbc .port{ width:22px; height:8px; border-radius:4px; background:#0a0c0f; border:1px solid #000; box-shadow:inset 0 0 2px #000 }
|
||||
.io{ display:flex; align-items:flex-start; justify-content:space-between; gap:6px; margin:0 2px;
|
||||
padding:9px 8px 8px; border-radius:9px; background:#0c0f14; border:1px solid #05070a; box-shadow:inset 0 1px 3px rgba(0,0,0,.6) }
|
||||
.jack{ flex:1; display:flex; flex-direction:column; align-items:center; gap:5px }
|
||||
.jack i{ width:20px; height:20px; border-radius:50%; background:radial-gradient(circle at 40% 34%, #333a44, #07090c 72%);
|
||||
border:2px solid #5b6470; box-shadow:inset 0 0 4px #000 }
|
||||
.jack b{ font-size:7.5px; font-weight:700; color:var(--silk); letter-spacing:.05em; text-transform:uppercase; opacity:.85; text-align:center; line-height:1.3 }
|
||||
.jack.usb i{ width:24px; height:10px; border-radius:4px; border:2px solid #5b6470; background:#07090c; margin-top:5px }
|
||||
|
||||
/* ---- load panel (same as the other pages) ---- */
|
||||
.panel{ width:100%; max-width:560px; background:var(--panel-bg); border:1px solid var(--panel-bd); border-radius:14px; padding:16px }
|
||||
.panel{ width:100%; max-width:380px; background:var(--panel-bg); border:1px solid var(--panel-bd); border-radius:14px; padding:16px }
|
||||
.panel h2{ margin:0 0 4px; font-size:15px }
|
||||
.panel p.sub{ margin:0 0 12px; font-size:12px; color:var(--muted); line-height:1.45 }
|
||||
textarea{ width:100%; background:var(--field-bg); color:var(--txt); border:1px solid var(--field-bd); border-radius:9px;
|
||||
|
|
@ -170,19 +184,25 @@
|
|||
<div class="ledbar-cap">4×16 WS2812 — beat (bottom) + 3 subdivision rows</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="keys">
|
||||
<div class="key"><button class="cap" id="bPrev" title="previous item">⏮</button><small>Prev</small></div>
|
||||
<div class="key"><button class="cap play" id="bPlay" title="play / stop (Space)">▶</button><small>Play</small></div>
|
||||
<div class="key"><button class="cap" id="bNext" title="next item">⏭</button><small>Next</small></div>
|
||||
<div class="key"><button class="cap" id="bTap" title="tap tempo (T)">TAP</button><small>Tap</small></div>
|
||||
</div>
|
||||
<div class="enc-wrap"><div class="enc" id="enc" title="Tempo — scroll or drag to turn"></div><small>TEMPO</small></div>
|
||||
<div class="keys">
|
||||
<div class="key"><button class="abtn nav" id="bPrev" title="previous item">⏮</button><small>Prev</small></div>
|
||||
<div class="key-mid">
|
||||
<div class="key"><button class="abtn play" id="bPlay" title="play / stop (Space)">▶</button><small>Play</small></div>
|
||||
<div class="key"><button class="abtn tap" id="bTap" title="tap tempo (T)">TAP</button><small>Tap</small></div>
|
||||
</div>
|
||||
<div class="key"><button class="abtn nav" id="bNext" title="next item">⏭</button><small>Next</small></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footrow">
|
||||
<div class="grille"></div>
|
||||
<div class="usbc"><span class="port"></span>USB‑C</div>
|
||||
<div class="io">
|
||||
<div class="jack" title="External trigger in — footswitch to start/stop or tap tempo"><i></i><b>Trig In</b></div>
|
||||
<div class="jack" title="Instrument in — 1/4" pass-through; the click is mixed into your signal"><i></i><b>Inst In</b></div>
|
||||
<div class="jack" title="Main out — 1/4" balanced TRS (instrument + click); the shared output plug"><i></i><b>Out TRS</b></div>
|
||||
<div class="jack usb" title="USB-C — power & set-list transfer"><i></i><b>USB-C</b></div>
|
||||
</div>
|
||||
<div class="ledbar-cap">Trig in · 1/4″ inst pass‑through (click injected) · shared 1/4″ balanced‑TRS out</div>
|
||||
</div>
|
||||
|
||||
<!-- ===================== LOAD CONFIG ===================== -->
|
||||
|
|
|
|||
Loading…
Reference in a new issue