Per-version info pages; move Stage BOM out to info-stage; add Micro BOM
Each form factor now has an information page (purpose + live embedded widget), with priced BOMs only on the buildable hardware (Stage ≈$59, Micro ≈$28): - info-editor.html / info-initial.html — purpose only (web app / concept) - info-stage.html — purpose + the priced BOM moved out of stage.html - info-micro.html — purpose + a new ~$28 practice-unit BOM stage.html drops the BOM panel (+ its .bom CSS) and gains a "Spec & BOM" link; the shared .bom/.sub table CSS lives in src/base.css. "Info" added to every page nav and to the concept cards. Wired into build.sh + deploy.sh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3e1ba98cb0
commit
d78fb762e5
12 changed files with 454 additions and 56 deletions
3
build.sh
3
build.sh
|
|
@ -30,7 +30,8 @@ def build(name):
|
||||||
out.write_text(src)
|
out.write_text(src)
|
||||||
return out.stat().st_size
|
return out.stat().st_size
|
||||||
|
|
||||||
for name in ("index.html","player.html","stage.html","micro.html","concepts.html","embed.html"):
|
for name in ("index.html","player.html","stage.html","micro.html","concepts.html","embed.html",
|
||||||
|
"info-editor.html","info-initial.html","info-stage.html","info-micro.html"):
|
||||||
print("built %s (%dKB)" % (name, build(name) // 1024))
|
print("built %s (%dKB)" % (name, build(name) // 1024))
|
||||||
pathlib.Path("dist/embed.js").write_text(pathlib.Path("embed.js").read_text()) # loader, served as-is
|
pathlib.Path("dist/embed.js").write_text(pathlib.Path("embed.js").read_text()) # loader, served as-is
|
||||||
print("copied embed.js")
|
print("copied embed.js")
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
<h3>PE‑1 — PolyMeter Editor</h3>
|
<h3>PE‑1 — PolyMeter Editor</h3>
|
||||||
<p>The full editor: stack meter lanes, per‑step accents / ghosts / mutes, swing & ratio polyrhythm,
|
<p>The full editor: stack meter lanes, per‑step accents / ghosts / mutes, swing & ratio polyrhythm,
|
||||||
set lists, and shareable links. This is where you design grooves.</p>
|
set lists, and shareable links. This is where you design grooves.</p>
|
||||||
<div class="links"><a href="/index.html">Open ↗</a></div>
|
<div class="links"><a href="/index.html">Open ↗</a><a href="/info-editor.html">Info ⓘ</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
<h3>PM‑1 — Initial</h3>
|
<h3>PM‑1 — Initial</h3>
|
||||||
<p>The original idealized device mock — full multi‑lane display and set‑list navigation. A north‑star
|
<p>The original idealized device mock — full multi‑lane display and set‑list navigation. A north‑star
|
||||||
concept (more than a single small unit can really show); the buildable take is Stage.</p>
|
concept (more than a single small unit can really show); the buildable take is Stage.</p>
|
||||||
<div class="links"><a href="/player.html">Open ↗</a></div>
|
<div class="links"><a href="/player.html">Open ↗</a><a href="/info-initial.html">Info ⓘ</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
<h3>PM‑1 — Stage</h3>
|
<h3>PM‑1 — Stage</h3>
|
||||||
<p>Pedalboard build: 2.0″ colour TFT, arcade buttons, thumb‑roller, 1/4″ instrument pass‑through with
|
<p>Pedalboard build: 2.0″ colour TFT, arcade buttons, thumb‑roller, 1/4″ instrument pass‑through with
|
||||||
analog click injection + balanced‑TRS out, 9 V DC / USB‑C. Bead‑blasted matte‑black anodised.</p>
|
analog click injection + balanced‑TRS out, 9 V DC / USB‑C. Bead‑blasted matte‑black anodised.</p>
|
||||||
<div class="links"><a href="/stage.html">Open ↗</a></div>
|
<div class="links"><a href="/stage.html">Open ↗</a><a href="/info-stage.html">Info & BOM ⓘ</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
<h3>PM‑µ — Micro</h3>
|
<h3>PM‑µ — Micro</h3>
|
||||||
<p>Minimal home‑practice unit: one push scroll‑encoder, a red 7‑segment LED, a speaker and USB‑C.
|
<p>Minimal home‑practice unit: one push scroll‑encoder, a red 7‑segment LED, a speaker and USB‑C.
|
||||||
Spin = tempo · press = start/stop · hold + spin = switch track.</p>
|
Spin = tempo · press = start/stop · hold + spin = switch track.</p>
|
||||||
<div class="links"><a href="/micro.html">Open ↗</a></div>
|
<div class="links"><a href="/micro.html">Open ↗</a><a href="/info-micro.html">Info & BOM ⓘ</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card soon">
|
<div class="card soon">
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ fi
|
||||||
|
|
||||||
# stamp the version into the built copy only (source stays clean)
|
# stamp the version into the built copy only (source stays clean)
|
||||||
echo "deployed v$BUILD -> $DEST_DIR"
|
echo "deployed v$BUILD -> $DEST_DIR"
|
||||||
for f in index.html player.html stage.html micro.html concepts.html embed.html; do
|
for f in index.html player.html stage.html micro.html concepts.html embed.html \
|
||||||
|
info-editor.html info-initial.html info-stage.html info-micro.html; do
|
||||||
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f"
|
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f"
|
||||||
echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)"
|
echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,7 @@
|
||||||
<h1 style="margin:0">PE‑1 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1>
|
<h1 style="margin:0">PE‑1 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1>
|
||||||
<div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px">
|
<div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px">
|
||||||
<a href="/concepts.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Concept gallery — hardware & widget form factors">Concepts</a>
|
<a href="/concepts.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Concept gallery — hardware & widget form factors">Concepts</a>
|
||||||
|
<a href="/info-editor.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="About the PolyMeter Editor">Info</a>
|
||||||
<a href="/embed.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Embed a PolyMeter widget">Embed</a>
|
<a href="/embed.html" style="font-size:13px; color:var(--muted); text-decoration:none" title="Embed a PolyMeter widget">Embed</a>
|
||||||
<button id="themeBtn" title="toggle light / dark theme">☀</button>
|
<button id="themeBtn" title="toggle light / dark theme">☀</button>
|
||||||
<button id="helpBtn" title="keyboard shortcuts (?)">?</button>
|
<button id="helpBtn" title="keyboard shortcuts (?)">?</button>
|
||||||
|
|
|
||||||
99
info-editor.html
Normal file
99
info-editor.html
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>PE‑1 PolyMeter Editor — info — VARASYS</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||||
|
<script>
|
||||||
|
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||||
|
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||||
|
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||||
|
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
/*@BUILD:include:src/base.css@*/
|
||||||
|
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||||
|
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||||
|
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||||
|
a{ color:var(--link); }
|
||||||
|
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||||
|
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||||
|
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||||
|
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||||
|
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||||
|
.tag.app{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||||
|
ul{ color:var(--muted); font-size:14px; line-height:1.7; padding-left:20px; } ul b{ color:var(--txt); }
|
||||||
|
.embed-wrap{ margin:16px 0 4px; }
|
||||||
|
.cap{ font-size:12px; color:var(--muted); }
|
||||||
|
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="site-head">
|
||||||
|
<div class="head-left">
|
||||||
|
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||||
|
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
</a>
|
||||||
|
<span class="page-name"><b>PE‑1</b> · PolyMeter Editor — info</span>
|
||||||
|
</div>
|
||||||
|
<nav class="site-nav">
|
||||||
|
<a href="/index.html">Open ↗</a>
|
||||||
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/embed.html">Embed</a>
|
||||||
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>PE‑1 — PolyMeter Editor</h1>
|
||||||
|
<div class="tags"><span class="tag app">Web app</span><span class="tag">Free · no install</span><span class="tag">Open source</span></div>
|
||||||
|
<p class="lead">The full polymeter workbench, and the reference design for the whole PolyMeter family: build
|
||||||
|
grooves where each voice runs in its own meter and subdivision, audition them in the browser, and save the
|
||||||
|
result as a compact program string the hardware units play back verbatim.</p>
|
||||||
|
|
||||||
|
<div class="embed-wrap">
|
||||||
|
<div data-varasys-metronome="editor"
|
||||||
|
data-patch="v1;t120;b16;kick:4=X..x;snare:4=.X.X;hatClosed:4/4;tom:3" data-height="680"></div>
|
||||||
|
</div>
|
||||||
|
<p class="cap">Live widget (embedded). <a href="/index.html">Open the full editor ↗</a> · <a href="/embed.html">embed this</a></p>
|
||||||
|
|
||||||
|
<h2>Designed for</h2>
|
||||||
|
<p>Anyone working in odd or layered meter — practising a 4‑over‑3, programming a polyrhythmic groove, or
|
||||||
|
sketching parts before a rehearsal. It's the authoring tool: everything the hardware devices play is
|
||||||
|
created here and shared as a program string or set‑list code.</p>
|
||||||
|
<ul>
|
||||||
|
<li><b>Per‑lane meter & subdivision</b> — each voice has its own beat count and subdivision, with a
|
||||||
|
pad grid for accents, ghosts, and mutes.</li>
|
||||||
|
<li><b>Drum & click voices</b> — synth and sampled sounds, swing, accent levels.</li>
|
||||||
|
<li><b>Trainer & ramp</b> — bar‑count automation, tempo ramps, and a countdown.</li>
|
||||||
|
<li><b>Program string</b> — the live patch is shown, editable, and copy/paste‑able; set‑lists encode to a
|
||||||
|
shareable code. This is the same string the <a href="/info-stage.html">Stage</a> and
|
||||||
|
<a href="/info-micro.html">Micro</a> units load.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="sub" style="margin-top:18px">A web app — nothing to build, nothing to buy. (Bills of materials apply
|
||||||
|
only to the buildable hardware: <a href="/info-stage.html">Stage</a> and <a href="/info-micro.html">Micro</a>.)</p>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="site-foot">VARASYS · Simplifying Complexity — <span id="appVersion">v0.0.1-dev</span></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const APP_VERSION = "v0.0.1-dev";
|
||||||
|
const $ = (id)=>document.getElementById(id);
|
||||||
|
try{ $("appVersion").textContent = "v"+APP_VERSION.replace(/^v/,""); }catch(e){}
|
||||||
|
const THEMES=["system","light","dark"];
|
||||||
|
function effectiveTheme(p){ return p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches?"light":"dark") : p; }
|
||||||
|
function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); return (p==="light"||p==="dark"||p==="system")?p:"system"; }catch(e){ return "system"; } }
|
||||||
|
function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){}
|
||||||
|
document.documentElement.dataset.theme = effectiveTheme(p);
|
||||||
|
$("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; $("themeBtn").title="Theme: "+p; }
|
||||||
|
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||||
|
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||||
|
applyTheme(themePref());
|
||||||
|
</script>
|
||||||
|
<script src="/embed.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
95
info-initial.html
Normal file
95
info-initial.html
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>PM‑1 Initial — info — VARASYS</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||||
|
<script>
|
||||||
|
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||||
|
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||||
|
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||||
|
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
/*@BUILD:include:src/base.css@*/
|
||||||
|
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||||
|
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||||
|
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||||
|
a{ color:var(--link); }
|
||||||
|
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||||
|
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||||
|
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||||
|
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||||
|
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||||
|
.tag.concept{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||||
|
.embed-wrap{ margin:16px 0 4px; }
|
||||||
|
.cap{ font-size:12px; color:var(--muted); }
|
||||||
|
.note{ margin-top:18px; padding:12px 14px; border:1px solid var(--panel-bd); border-radius:10px; background:var(--panel-bg); }
|
||||||
|
.note p{ margin:0; }
|
||||||
|
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="site-head">
|
||||||
|
<div class="head-left">
|
||||||
|
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||||
|
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
</a>
|
||||||
|
<span class="page-name"><b>PM‑1</b> · Initial — info</span>
|
||||||
|
</div>
|
||||||
|
<nav class="site-nav">
|
||||||
|
<a href="/index.html">Editor</a>
|
||||||
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/player.html">Open ↗</a>
|
||||||
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>PM‑1 — Initial</h1>
|
||||||
|
<div class="tags"><span class="tag concept">Concept</span><span class="tag">Idealized device</span><span class="tag">Not buildable as drawn</span></div>
|
||||||
|
<p class="lead">The idealized PM‑1: the player as a clean, screen‑first device with no concession to
|
||||||
|
mechanical parts yet. It's the look we design <i>toward</i> — the <a href="/info-stage.html">Stage</a>
|
||||||
|
build is what that idea becomes once it's made from real components.</p>
|
||||||
|
|
||||||
|
<div class="embed-wrap">
|
||||||
|
<div data-varasys-metronome="initial"
|
||||||
|
data-patch="v1;t120;b16;kick:4=X.x.;snare:4=.X.X;hatClosed:4/4" data-height="560"></div>
|
||||||
|
</div>
|
||||||
|
<p class="cap">Live widget (embedded). <a href="/player.html">Open the full Initial page ↗</a> · <a href="/embed.html">embed this</a></p>
|
||||||
|
|
||||||
|
<h2>What it is</h2>
|
||||||
|
<p>A concept render of a dedicated PolyMeter player: full set‑list navigation, a colour beat display showing
|
||||||
|
every lane, light/dark theming, and a fullscreen landscape "stage" view. It runs the same engine and program
|
||||||
|
strings as everything else in the family, but as an <i>idealized</i> object — before deciding which buttons,
|
||||||
|
encoders, jacks, and enclosure actually make it real.</p>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<p>This is a concept, so there's <b>no bill of materials</b> — there's nothing to source for a render. The
|
||||||
|
buildable realization of this idea is the <a href="/info-stage.html">PM‑1 Stage</a>, which has a full priced
|
||||||
|
BOM. For the smallest practical unit, see <a href="/info-micro.html">PM‑µ Micro</a>.</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="site-foot">VARASYS · Simplifying Complexity — <span id="appVersion">v0.0.1-dev</span></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const APP_VERSION = "v0.0.1-dev";
|
||||||
|
const $ = (id)=>document.getElementById(id);
|
||||||
|
try{ $("appVersion").textContent = "v"+APP_VERSION.replace(/^v/,""); }catch(e){}
|
||||||
|
const THEMES=["system","light","dark"];
|
||||||
|
function effectiveTheme(p){ return p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches?"light":"dark") : p; }
|
||||||
|
function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); return (p==="light"||p==="dark"||p==="system")?p:"system"; }catch(e){ return "system"; } }
|
||||||
|
function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){}
|
||||||
|
document.documentElement.dataset.theme = effectiveTheme(p);
|
||||||
|
$("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; $("themeBtn").title="Theme: "+p; }
|
||||||
|
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||||
|
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||||
|
applyTheme(themePref());
|
||||||
|
</script>
|
||||||
|
<script src="/embed.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
107
info-micro.html
Normal file
107
info-micro.html
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>PM‑µ Micro — info & BOM — VARASYS</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||||
|
<script>
|
||||||
|
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||||
|
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||||
|
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||||
|
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
/*@BUILD:include:src/base.css@*/
|
||||||
|
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||||
|
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||||
|
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||||
|
a{ color:var(--link); }
|
||||||
|
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||||
|
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||||
|
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||||
|
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||||
|
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||||
|
.tag.hw{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||||
|
.embed-wrap{ margin:16px 0 4px; }
|
||||||
|
.cap{ font-size:12px; color:var(--muted); }
|
||||||
|
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="site-head">
|
||||||
|
<div class="head-left">
|
||||||
|
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||||
|
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
</a>
|
||||||
|
<span class="page-name"><b>PM‑µ</b> · Micro — info</span>
|
||||||
|
</div>
|
||||||
|
<nav class="site-nav">
|
||||||
|
<a href="/index.html">Editor</a>
|
||||||
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/micro.html">Open ↗</a>
|
||||||
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>PM‑µ — Micro</h1>
|
||||||
|
<div class="tags"><span class="tag hw">Hardware</span><span class="tag">Home practice</span><span class="tag">~$28 one‑off</span></div>
|
||||||
|
<p class="lead">The smallest possible polymeter unit for daily practice: one dial and a red LED, nothing to
|
||||||
|
learn. Spin for tempo, press to start/stop, hold & spin to flip through grooves — that's it.</p>
|
||||||
|
|
||||||
|
<div class="embed-wrap">
|
||||||
|
<div data-varasys-metronome="micro"
|
||||||
|
data-patch="v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2" data-height="300"></div>
|
||||||
|
</div>
|
||||||
|
<p class="cap">Live widget (embedded). <a href="/micro.html">Open the full Micro page ↗</a> · <a href="/embed.html">embed this</a></p>
|
||||||
|
|
||||||
|
<h2>Designed for</h2>
|
||||||
|
<p>The practice desk. No screen to read, no menus — a single depressable scroll‑encoder does everything
|
||||||
|
(spin = tempo, press = start/stop, hold + spin = switch track) and a bright 7‑segment LED shows the BPM
|
||||||
|
(or the track number while you switch). It runs off any USB‑C charger, plays through a small built‑in
|
||||||
|
speaker, and ships with the editor's grooves built in. Synth voices only — no analog audio path.</p>
|
||||||
|
|
||||||
|
<h2>Bill of materials</h2>
|
||||||
|
<p class="sub">Rough parts list — a USB‑C‑powered RP2040 practice unit. Ballpark one‑off prices (USD); cheaper at volume.</p>
|
||||||
|
<table class="bom">
|
||||||
|
<thead><tr><th>Part</th><th class="q">Qty</th><th class="c">~$</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="grp"><td colspan="3">Brain & display</td></tr>
|
||||||
|
<tr><td class="part">RP2040 board, USB‑C <span class="spec">— e.g. Waveshare RP2040‑Zero</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||||
|
<tr><td class="part">3‑digit 7‑segment LED (red) + driver <span class="spec">— MAX7219 / shift register, or direct GPIO</span></td><td class="q">1</td><td class="c">3</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Control</td></tr>
|
||||||
|
<tr><td class="part">Detented push encoder (EC11) + knob <span class="spec">— tempo / press / hold‑spin</span></td><td class="q">1</td><td class="c">2</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Audio</td></tr>
|
||||||
|
<tr><td class="part">MAX98357A I²S amp + 8 Ω 2 W speaker <span class="spec">— synth click (no analog in)</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Power & build</td></tr>
|
||||||
|
<tr><td class="part">USB‑C bus power (on the board) + PWR LED</td><td class="q">1</td><td class="c">1</td></tr>
|
||||||
|
<tr><td class="part">Custom PCB (or perfboard)</td><td class="q">1</td><td class="c">4</td></tr>
|
||||||
|
<tr><td class="part">Passives, headers, wire</td><td class="q">—</td><td class="c">2</td></tr>
|
||||||
|
<tr><td class="part">Small aluminium enclosure <span class="spec">— bead‑blasted, matte‑black anodised</span></td><td class="q">1</td><td class="c">8</td></tr>
|
||||||
|
<tr class="total"><td>Total (one‑off)</td><td class="q"></td><td class="c">≈ $28</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="site-foot">VARASYS · Simplifying Complexity — <span id="appVersion">v0.0.1-dev</span></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const APP_VERSION = "v0.0.1-dev";
|
||||||
|
const $ = (id)=>document.getElementById(id);
|
||||||
|
try{ $("appVersion").textContent = "v"+APP_VERSION.replace(/^v/,""); }catch(e){}
|
||||||
|
const THEMES=["system","light","dark"];
|
||||||
|
function effectiveTheme(p){ return p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches?"light":"dark") : p; }
|
||||||
|
function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); return (p==="light"||p==="dark"||p==="system")?p:"system"; }catch(e){ return "system"; } }
|
||||||
|
function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){}
|
||||||
|
document.documentElement.dataset.theme = effectiveTheme(p);
|
||||||
|
$("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; $("themeBtn").title="Theme: "+p; }
|
||||||
|
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||||
|
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||||
|
applyTheme(themePref());
|
||||||
|
</script>
|
||||||
|
<script src="/embed.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
121
info-stage.html
Normal file
121
info-stage.html
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>PM‑1 Stage — info & BOM — VARASYS</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||||
|
<script>
|
||||||
|
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||||
|
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||||
|
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||||
|
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
/*@BUILD:include:src/base.css@*/
|
||||||
|
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||||
|
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||||
|
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||||
|
a{ color:var(--link); }
|
||||||
|
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||||
|
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||||
|
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||||
|
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||||
|
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||||
|
.tag.hw{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||||
|
.embed-wrap{ margin:16px 0 4px; }
|
||||||
|
.cap{ font-size:12px; color:var(--muted); }
|
||||||
|
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="site-head">
|
||||||
|
<div class="head-left">
|
||||||
|
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||||
|
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||||
|
</a>
|
||||||
|
<span class="page-name"><b>PM‑1</b> · Stage — info</span>
|
||||||
|
</div>
|
||||||
|
<nav class="site-nav">
|
||||||
|
<a href="/index.html">Editor</a>
|
||||||
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/stage.html">Open ↗</a>
|
||||||
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>PM‑1 — Stage</h1>
|
||||||
|
<div class="tags"><span class="tag hw">Hardware</span><span class="tag">Pedalboard</span><span class="tag">~$59 one‑off</span></div>
|
||||||
|
<p class="lead">A gig‑ready polymeter box for the pedalboard: a colour readout of every lane, fast set‑list
|
||||||
|
navigation, and your instrument running straight through with the click mixed in — so it sits in your
|
||||||
|
signal chain and feeds the PA, all on a non‑reflective matte‑black case.</p>
|
||||||
|
|
||||||
|
<div class="embed-wrap">
|
||||||
|
<div data-varasys-metronome="stage"
|
||||||
|
data-patch="v1;t120;b16;kick:4=X.x.;snare:4=.X.X;hatClosed:4/4" data-height="520"></div>
|
||||||
|
</div>
|
||||||
|
<p class="cap">Live widget (embedded). <a href="/stage.html">Open the full Stage page ↗</a> · <a href="/embed.html">embed this</a></p>
|
||||||
|
|
||||||
|
<h2>Designed for</h2>
|
||||||
|
<p>The stage and the rehearsal room. Top‑mounted 1/4″ jacks keep cabling tidy on a board; you plug your
|
||||||
|
instrument in, the metronome click is summed into the signal in the <b>analog domain</b> (no re‑digitising,
|
||||||
|
no added latency) and sent to a balanced 1/4″ TRS output for the desk, plus a small monitor speaker.
|
||||||
|
Powered from a standard 9 V DC pedal supply or USB‑C. The colour TFT shows tempo, the item name, and all
|
||||||
|
lane patterns; arcade buttons + a recessed thumb‑roller survive stage abuse.</p>
|
||||||
|
|
||||||
|
<h2>Bill of materials</h2>
|
||||||
|
<p class="sub">Rough parts list — a pedalboard‑friendly RP2040 build (9 V DC or USB‑C) with analog click injection.
|
||||||
|
Ballpark one‑off prices (USD); cheaper at volume.</p>
|
||||||
|
<table class="bom">
|
||||||
|
<thead><tr><th>Part</th><th class="q">Qty</th><th class="c">~$</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="grp"><td colspan="3">Brain & display</td></tr>
|
||||||
|
<tr><td class="part">RP2040 board, USB‑C <span class="spec">— e.g. Waveshare RP2040‑Zero / Pico‑clone</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||||
|
<tr><td class="part">2.0″ 320×240 IPS TFT, ST7789 <span class="spec">— SPI</span></td><td class="q">1</td><td class="c">8</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Controls</td></tr>
|
||||||
|
<tr><td class="part">Arcade pushbutton, 24 mm <span class="spec">— Prev · Next · Tap</span></td><td class="q">3</td><td class="c">4</td></tr>
|
||||||
|
<tr><td class="part">Arcade pushbutton, 30 mm <span class="spec">— Play</span></td><td class="q">1</td><td class="c">2</td></tr>
|
||||||
|
<tr><td class="part">Detented encoder (EC11 / PEC12) + side‑mount thumb‑roller <span class="spec">— recessed; nothing to snap off</span></td><td class="q">1</td><td class="c">2</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Audio — analog click injection</td></tr>
|
||||||
|
<tr><td class="part">PCM5102A I²S DAC <span class="spec">— line‑level click</span></td><td class="q">1</td><td class="c">3</td></tr>
|
||||||
|
<tr><td class="part">Dual op‑amp, NE5532 / OPA2134 <span class="spec">— hi‑Z instrument buffer + summing mixer</span></td><td class="q">1</td><td class="c">1</td></tr>
|
||||||
|
<tr><td class="part">Balanced line driver, DRV134 <span class="spec">— (or cross‑coupled op‑amp) → 1/4″ TRS out</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||||
|
<tr><td class="part">PAM8302A mono Class‑D + 8 Ω 2 W speaker <span class="spec">— monitor</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Connectors & power</td></tr>
|
||||||
|
<tr><td class="part">1/4″ jack <span class="spec">— Inst In (TS) · Out (TRS) · Trig In (TS)</span></td><td class="q">3</td><td class="c">3</td></tr>
|
||||||
|
<tr><td class="part">USB‑C bus power (5 V) + PWR LED <span class="spec">— same port carries config; no battery</span></td><td class="q">1</td><td class="c">1</td></tr>
|
||||||
|
<tr><td class="part">9 V DC pedal jack (2.1 mm centre‑neg) + 9 V→5 V buck + reverse‑polarity protect <span class="spec">— pedalboard power</span></td><td class="q">1</td><td class="c">3</td></tr>
|
||||||
|
<tr class="grp"><td colspan="3">Build</td></tr>
|
||||||
|
<tr><td class="part">Custom PCB (or perfboard)</td><td class="q">1</td><td class="c">5</td></tr>
|
||||||
|
<tr><td class="part">Passives, headers, wire <span class="spec">— R/C for the analog stage + decoupling</span></td><td class="q">—</td><td class="c">3</td></tr>
|
||||||
|
<tr><td class="part">Die‑cast aluminium enclosure (Hammond 1590‑style) <span class="spec">— bead‑blasted, matte‑black Type II anodise, laser‑etched legends</span></td><td class="q">1</td><td class="c">12</td></tr>
|
||||||
|
<tr class="total"><td>Total (one‑off)</td><td class="q"></td><td class="c">≈ $59</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="sub" style="margin-top:12px">Audio is summed in the <b>analog domain</b>: the DAC's click is mixed with a high‑impedance
|
||||||
|
buffer of the 1/4″ instrument input, then fed to the balanced line driver (1/4″ TRS out) and the monitor amp —
|
||||||
|
so your instrument is never re‑digitised (no added latency).</p>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="site-foot">VARASYS · Simplifying Complexity — <span id="appVersion">v0.0.1-dev</span></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const APP_VERSION = "v0.0.1-dev";
|
||||||
|
const $ = (id)=>document.getElementById(id);
|
||||||
|
try{ $("appVersion").textContent = "v"+APP_VERSION.replace(/^v/,""); }catch(e){}
|
||||||
|
const THEMES=["system","light","dark"];
|
||||||
|
function effectiveTheme(p){ return p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches?"light":"dark") : p; }
|
||||||
|
function themePref(){ try{ const p=localStorage.getItem("metronome.theme"); return (p==="light"||p==="dark"||p==="system")?p:"system"; }catch(e){ return "system"; } }
|
||||||
|
function applyTheme(p){ try{ localStorage.setItem("metronome.theme",p); }catch(e){}
|
||||||
|
document.documentElement.dataset.theme = effectiveTheme(p);
|
||||||
|
$("themeBtn").textContent = p==="system" ? "◐" : p==="light" ? "☀" : "☾"; $("themeBtn").title="Theme: "+p; }
|
||||||
|
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||||
|
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||||
|
applyTheme(themePref());
|
||||||
|
</script>
|
||||||
|
<script src="/embed.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -110,6 +110,7 @@
|
||||||
<nav class="site-nav">
|
<nav class="site-nav">
|
||||||
<a href="/index.html">Editor</a>
|
<a href="/index.html">Editor</a>
|
||||||
<a href="/concepts.html">Concepts</a>
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/info-micro.html">Info</a>
|
||||||
<a href="/embed.html">Embed</a>
|
<a href="/embed.html">Embed</a>
|
||||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,7 @@
|
||||||
<nav class="site-nav">
|
<nav class="site-nav">
|
||||||
<a href="/index.html">Editor</a>
|
<a href="/index.html">Editor</a>
|
||||||
<a href="/concepts.html">Concepts</a>
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/info-initial.html">Info</a>
|
||||||
<a href="/embed.html">Embed</a>
|
<a href="/embed.html">Embed</a>
|
||||||
<button id="fsBtn" class="tbtn" title="Full screen (landscape)">⛶</button>
|
<button id="fsBtn" class="tbtn" title="Full screen (landscape)">⛶</button>
|
||||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
|
|
|
||||||
12
src/base.css
12
src/base.css
|
|
@ -33,3 +33,15 @@ body {
|
||||||
The flag is set on <html> in a head pre-paint script (no flash). */
|
The flag is set on <html> in a head pre-paint script (no flash). */
|
||||||
[data-embed] .site-head, [data-embed] .site-foot { display:none !important; }
|
[data-embed] .site-head, [data-embed] .site-foot { display:none !important; }
|
||||||
[data-embed] body { padding:10px !important; }
|
[data-embed] body { padding:10px !important; }
|
||||||
|
|
||||||
|
/* ---- bill-of-materials table (info pages) ---- */
|
||||||
|
.sub { color:var(--muted,#7f8b9a); font-size:13px; line-height:1.5; }
|
||||||
|
.bom { width:100%; border-collapse:collapse; font-size:12px; margin-top:8px; }
|
||||||
|
.bom th, .bom td { text-align:left; padding:5px 6px; border-bottom:1px solid var(--panel-bd,#2a313c); vertical-align:top; }
|
||||||
|
.bom th { color:var(--muted,#7f8b9a); font-weight:600; font-size:10px; text-transform:uppercase; letter-spacing:.05em; }
|
||||||
|
.bom th.q, .bom th.c, .bom td.q, .bom td.c { text-align:right; white-space:nowrap; }
|
||||||
|
.bom td.q, .bom td.c { color:var(--muted,#7f8b9a); }
|
||||||
|
.bom .grp td { color:var(--cyan); font-weight:700; font-size:10px; text-transform:uppercase; letter-spacing:.07em; padding-top:11px; }
|
||||||
|
.bom .part { color:var(--txt,#c7d0db); }
|
||||||
|
.bom .part .spec { color:var(--muted,#7f8b9a); font-weight:400; }
|
||||||
|
.bom tr.total td { font-weight:700; color:var(--txt,#c7d0db); border-top:2px solid var(--panel-bd,#2a313c); border-bottom:none; padding-top:8px; }
|
||||||
|
|
|
||||||
59
stage.html
59
stage.html
|
|
@ -102,10 +102,9 @@
|
||||||
/* small caption under the screen / I/O (the beat indicator lives on the TFT now) */
|
/* small caption under the screen / I/O (the beat indicator lives on the TFT now) */
|
||||||
.ledbar-cap{ text-align:center; font-size:10px; color:var(--muted); margin:2px 0 0; letter-spacing:.02em }
|
.ledbar-cap{ text-align:center; font-size:10px; color:var(--muted); margin:2px 0 0; letter-spacing:.02em }
|
||||||
|
|
||||||
/* side-by-side: player on the left, BOM on the right (stacks when narrow) */
|
/* device column (centred; the priced BOM now lives on the info page) */
|
||||||
.cols{ display:flex; flex-wrap:wrap; align-items:flex-start; justify-content:center; gap:18px; width:100% }
|
.cols{ display:flex; flex-wrap:wrap; align-items:flex-start; justify-content:center; gap:18px; width:100% }
|
||||||
.col-left{ display:flex; flex-direction:column; align-items:center; gap:14px; width:380px; max-width:100% }
|
.col-left{ display:flex; flex-direction:column; align-items:center; gap:14px; width:380px; max-width:100% }
|
||||||
.bom-panel{ width:380px; max-width:100%; align-self:flex-start }
|
|
||||||
|
|
||||||
/* ---- top-edge view: all connectors on the top + total thickness ---- */
|
/* ---- top-edge view: all connectors on the top + total thickness ---- */
|
||||||
.topview{ width:380px; max-width:100%; display:flex; flex-direction:column; gap:5px }
|
.topview{ width:380px; max-width:100%; display:flex; flex-direction:column; gap:5px }
|
||||||
|
|
@ -190,18 +189,11 @@
|
||||||
.hint{ font-size:11px; color:var(--muted) }
|
.hint{ font-size:11px; color:var(--muted) }
|
||||||
code{ background:var(--field-bg); border:1px solid var(--field-bd); border-radius:4px; padding:1px 5px; font-size:11px }
|
code{ background:var(--field-bg); border:1px solid var(--field-bd); border-radius:4px; padding:1px 5px; font-size:11px }
|
||||||
|
|
||||||
/* ---- bill of materials ---- */
|
/* link out to the spec & BOM info page (the priced BOM lives there now) */
|
||||||
.bom{ width:100%; border-collapse:collapse; font-size:12px; margin-top:8px }
|
.speclink{ width:100%; max-width:380px; margin:12px 0 0; font-size:12px; color:var(--muted); line-height:1.5 }
|
||||||
.bom th, .bom td{ text-align:left; padding:5px 6px; border-bottom:1px solid var(--panel-bd); vertical-align:top }
|
.speclink a{ color:var(--link); font-weight:600 }
|
||||||
.bom th{ color:var(--muted); font-weight:600; font-size:10px; text-transform:uppercase; letter-spacing:.05em }
|
/* embed mode: just the device (drop the top-edge view, dims, loader, spec link) */
|
||||||
.bom th.q, .bom th.c, .bom td.q, .bom td.c{ text-align:right; white-space:nowrap }
|
[data-embed] .topview, [data-embed] .panel, [data-embed] .speclink,
|
||||||
.bom td.q, .bom td.c{ color:var(--muted) }
|
|
||||||
.bom .grp td{ color:var(--cyan); font-weight:700; font-size:10px; text-transform:uppercase; letter-spacing:.07em; padding-top:11px }
|
|
||||||
.bom .part{ color:var(--txt) }
|
|
||||||
.bom .part .spec{ color:var(--muted); font-weight:400 }
|
|
||||||
.bom tr.total td{ font-weight:700; color:var(--txt); border-top:2px solid var(--panel-bd); border-bottom:none; padding-top:8px }
|
|
||||||
/* embed mode: just the device (drop the top-edge view, dims, loader, BOM) */
|
|
||||||
[data-embed] .topview, [data-embed] .bom-panel, [data-embed] .panel,
|
|
||||||
[data-embed] .dim-y, [data-embed] .dim-x { display:none !important; }
|
[data-embed] .dim-y, [data-embed] .dim-x { display:none !important; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -218,6 +210,7 @@
|
||||||
<nav class="site-nav">
|
<nav class="site-nav">
|
||||||
<a href="/index.html">Editor</a>
|
<a href="/index.html">Editor</a>
|
||||||
<a href="/concepts.html">Concepts</a>
|
<a href="/concepts.html">Concepts</a>
|
||||||
|
<a href="/info-stage.html">Info</a>
|
||||||
<a href="/embed.html">Embed</a>
|
<a href="/embed.html">Embed</a>
|
||||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
@ -291,43 +284,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="status" id="status"></div>
|
<div class="status" id="status"></div>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /col-left -->
|
|
||||||
|
|
||||||
<!-- ===================== BILL OF MATERIALS ===================== -->
|
<p class="speclink"><a href="/info-stage.html">Spec & full priced BOM ⓘ</a> — what the Stage is designed for, and every part to build one.</p>
|
||||||
<div class="panel bom-panel">
|
</div><!-- /col-left -->
|
||||||
<h2>Bill of materials</h2>
|
|
||||||
<p class="sub">Rough parts list for the device above — a pedalboard‑friendly RP2040 build (9 V DC or USB‑C) with analog click injection.
|
|
||||||
Ballpark one-off prices (USD); cheaper at volume.</p>
|
|
||||||
<table class="bom">
|
|
||||||
<thead><tr><th>Part</th><th class="q">Qty</th><th class="c">~$</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="grp"><td colspan="3">Brain & display</td></tr>
|
|
||||||
<tr><td class="part">RP2040 board, USB‑C <span class="spec">— e.g. Waveshare RP2040‑Zero / Pico‑clone</span></td><td class="q">1</td><td class="c">4</td></tr>
|
|
||||||
<tr><td class="part">2.0″ 320×240 IPS TFT, ST7789 <span class="spec">— SPI</span></td><td class="q">1</td><td class="c">8</td></tr>
|
|
||||||
<tr class="grp"><td colspan="3">Controls</td></tr>
|
|
||||||
<tr><td class="part">Arcade pushbutton, 24 mm <span class="spec">— Prev · Next · Tap</span></td><td class="q">3</td><td class="c">4</td></tr>
|
|
||||||
<tr><td class="part">Arcade pushbutton, 30 mm <span class="spec">— Play</span></td><td class="q">1</td><td class="c">2</td></tr>
|
|
||||||
<tr><td class="part">Detented encoder (EC11 / PEC12) + side‑mount thumb‑roller <span class="spec">— recessed; nothing to snap off</span></td><td class="q">1</td><td class="c">2</td></tr>
|
|
||||||
<tr class="grp"><td colspan="3">Audio — analog click injection</td></tr>
|
|
||||||
<tr><td class="part">PCM5102A I²S DAC <span class="spec">— line‑level click</span></td><td class="q">1</td><td class="c">3</td></tr>
|
|
||||||
<tr><td class="part">Dual op‑amp, NE5532 / OPA2134 <span class="spec">— hi‑Z instrument buffer + summing mixer</span></td><td class="q">1</td><td class="c">1</td></tr>
|
|
||||||
<tr><td class="part">Balanced line driver, DRV134 <span class="spec">— (or cross‑coupled op‑amp) → 1/4″ TRS out</span></td><td class="q">1</td><td class="c">4</td></tr>
|
|
||||||
<tr><td class="part">PAM8302A mono Class‑D + 8 Ω 2 W speaker <span class="spec">— monitor</span></td><td class="q">1</td><td class="c">4</td></tr>
|
|
||||||
<tr class="grp"><td colspan="3">Connectors & power</td></tr>
|
|
||||||
<tr><td class="part">1/4″ jack <span class="spec">— Inst In (TS) · Out (TRS) · Trig In (TS)</span></td><td class="q">3</td><td class="c">3</td></tr>
|
|
||||||
<tr><td class="part">USB‑C bus power (5 V) + PWR LED <span class="spec">— same port carries config; no battery</span></td><td class="q">1</td><td class="c">1</td></tr>
|
|
||||||
<tr><td class="part">9 V DC pedal jack (2.1 mm centre‑neg) + 9 V→5 V buck + reverse‑polarity protect <span class="spec">— pedalboard power</span></td><td class="q">1</td><td class="c">3</td></tr>
|
|
||||||
<tr class="grp"><td colspan="3">Build</td></tr>
|
|
||||||
<tr><td class="part">Custom PCB (or perfboard)</td><td class="q">1</td><td class="c">5</td></tr>
|
|
||||||
<tr><td class="part">Passives, headers, wire <span class="spec">— R/C for the analog stage + decoupling</span></td><td class="q">—</td><td class="c">3</td></tr>
|
|
||||||
<tr><td class="part">Die‑cast aluminium enclosure (Hammond 1590‑style) <span class="spec">— bead‑blasted, matte‑black Type II anodise, laser‑etched legends</span></td><td class="q">1</td><td class="c">12</td></tr>
|
|
||||||
<tr class="total"><td>Total (one‑off)</td><td class="q"></td><td class="c">≈ $59</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p class="sub" style="margin-top:12px">Audio is summed in the <b>analog domain</b>: the DAC's click is mixed with a high‑impedance
|
|
||||||
buffer of the 1/4″ instrument input, then fed to the balanced line driver (1/4″ TRS out) and the monitor amp —
|
|
||||||
so your instrument is never re‑digitised (no added latency).</p>
|
|
||||||
</div>
|
|
||||||
</div><!-- /cols -->
|
</div><!-- /cols -->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue