Restructure (2a): shared chrome on every page; merge Open=Info into one page per form factor
- Shared header/footer/chrome (src/header.html, src/footer.html, src/chrome.js) now on every page: editor (header above its app toolbar), player, teacher, stage, micro, showcase, embed. chrome.js defers to DOMContentLoaded so the footer version stamps regardless of placement. Player's fullscreen toggle relocated out of the header to a floating control. - Open = Info: each form-factor page is self-contained — a more-detailed description (.about) + an expandable "Spec & BOM" (<details class="spec">, hidden in embed). info-*.html retired; build/deploy/README updated. Next: teacher-style dimensioned front + top/side views + loading panels for Stage, Micro and Showcase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e048be390f
commit
1c74100065
18 changed files with 256 additions and 841 deletions
17
README.md
17
README.md
|
|
@ -26,11 +26,14 @@ State (set lists, the practice log, theme and UI preferences) lives in `localSto
|
|||
| `/teacher.html` | **PM‑1 Teacher** — studio / lesson console (colour TFT, arcade buttons, 1/4″ instrument pass‑through with analog click injection) |
|
||||
| `/stage.html` | **PM‑1 Stage** — foot‑pedal stompbox (two footswitches, expression‑pedal in, RGB beat light, instrument pass‑through) |
|
||||
| `/micro.html` | **PM‑µ Micro** — inline practice bar (instrument in / out pass‑through, clickable thumb‑roller, 14‑segment display) |
|
||||
| `/showcase.html` | **PM‑S Showcase** — pyramid display piece with an RGB‑light pendulum + per‑lane subdivision/accent light rows |
|
||||
| `/info-editor.html`, `/info-initial.html` | purpose pages (web app / concept — no BOM) |
|
||||
| `/info-teacher.html`, `/info-stage.html`, `/info-micro.html`, `/info-showcase.html` | purpose **+ priced BOM** (buildable hardware only) |
|
||||
| `/showcase.html` | **PM‑S Showcase** — pyramid display piece; the pendulum is an RGB light bar combining every lane's subdivisions/accents |
|
||||
| `/embed.html` · `/embed.js` | embed docs and the drop‑in loader |
|
||||
|
||||
Each form‑factor page is self‑contained ("Open" = "Info"): a more‑detailed description,
|
||||
the live device with front + top/side dimensioned views and loading instructions, and an
|
||||
expandable **Spec & BOM** (priced, for the buildable hardware). The buildable units are the
|
||||
Teacher, Stage, Micro and Showcase; the Editor (web app) and Initial (concept) have no BOM.
|
||||
|
||||
Each page carries the same VARASYS header (logo + tagline, nav, theme toggle). The editor
|
||||
also shows a subtle live **program string** of what's loaded — editable, with copy/paste —
|
||||
under the app (press `Enter` or paste to apply; see [the share language](#the-share-language)).
|
||||
|
|
@ -158,8 +161,9 @@ container and the loader script — it builds an `<iframe>` to the chrome‑stri
|
|||
- `data-width` / `data-height` — optional initial size (default `100%` × `300px`;
|
||||
height then tracks the widget, which posts `{type:'varasys-h', h}` to the parent).
|
||||
|
||||
Prefer your own iframe? `…/<variant>.html?embed=1#p=<patch>` works directly. Our own
|
||||
[`info-*.html`](info-teacher.html) pages dogfood this exact mechanism. See `/embed.html`.
|
||||
Prefer your own iframe? `…/<variant>.html?embed=1#p=<patch>` works directly. The
|
||||
[Concepts landing](index.html) dogfoods this exact mechanism (every box is a live widget).
|
||||
See `/embed.html`.
|
||||
|
||||
## Keyboard shortcuts
|
||||
|
||||
|
|
@ -211,7 +215,7 @@ in sync:
|
|||
assets from `assets/`.
|
||||
|
||||
`./build.sh` resolves every marker into a self‑contained page in `dist/` — the editor +
|
||||
concepts + device mockups + the `info-*.html` pages — and copies `embed.js` through as‑is
|
||||
the Concepts landing + editor + device/form-factor pages — and copies `embed.js` through as‑is
|
||||
(the editor inlines the CC0 samples; the device pages pass an empty `SAMPLES` for pure synth).
|
||||
`dist/` is generated, git‑ignored — don't edit it by hand. `deploy.sh` runs the build first,
|
||||
so a deploy always serves freshly assembled pages.
|
||||
|
|
@ -235,7 +239,6 @@ Push the tag, then deploy.
|
|||
| `editor.html` | the **PE‑1 editor** app (source, with `@BUILD:*` markers) |
|
||||
| `src/header.html` · `src/footer.html` · `src/chrome.js` | shared header / footer / theme chrome, inlined into every page |
|
||||
| `player.html` · `teacher.html` · `stage.html` · `micro.html` · `showcase.html` | the device mockups (PM‑1 Initial / Teacher / Stage, PM‑µ Micro, PM‑S Showcase) |
|
||||
| `info-*.html` | per‑form‑factor info pages (purpose + priced BOM for buildable hardware) |
|
||||
| `embed.html` · `embed.js` | embed docs and the drop‑in widget loader |
|
||||
| `src/` | shared partials inlined into every page: `engine.js`, `setlists.js`, `base.css` |
|
||||
| `assets/` | base64 blobs inlined at build (samples, logos, favicon) |
|
||||
|
|
|
|||
3
build.sh
3
build.sh
|
|
@ -32,8 +32,7 @@ def build(name):
|
|||
return out.stat().st_size
|
||||
|
||||
for name in ("index.html","editor.html","player.html","teacher.html","stage.html","micro.html","showcase.html",
|
||||
"embed.html",
|
||||
"info-editor.html","info-initial.html","info-teacher.html","info-stage.html","info-micro.html","info-showcase.html"):
|
||||
"embed.html"):
|
||||
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
|
||||
print("copied embed.js")
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@ fi
|
|||
# stamp the version into the built copy only (source stays clean)
|
||||
echo "deployed v$BUILD -> $DEST_DIR"
|
||||
for f in index.html editor.html player.html teacher.html stage.html micro.html showcase.html \
|
||||
embed.html \
|
||||
info-editor.html info-initial.html info-teacher.html info-stage.html info-micro.html info-showcase.html; do
|
||||
embed.html; do
|
||||
sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f"
|
||||
echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)"
|
||||
done
|
||||
cp "$DIST_DIR/embed.js" "$DEST_DIR/embed.js"; echo " embed.js ($(stat -c '%s' "$DEST_DIR/embed.js") bytes)"
|
||||
rm -f "$DEST_DIR/player-asbuilt.html" # renamed to teacher.html
|
||||
rm -f "$DEST_DIR/concepts.html" # Concepts is now the landing (/)
|
||||
rm -f "$DEST_DIR"/info-*.html # info pages merged into each form-factor page
|
||||
# (stage.html / info-stage.html are deployed again — now the foot-pedal Stage stompbox)
|
||||
|
||||
# If real audio samples are added later (see the plan's GM-sample note),
|
||||
|
|
|
|||
31
editor.html
31
editor.html
|
|
@ -237,21 +237,15 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
|
||||
<div id="app">
|
||||
<div class="device">
|
||||
<div class="row appheader" style="align-items:center; flex-wrap:wrap; gap:6px 14px; margin-bottom:8px">
|
||||
<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></h1>
|
||||
<div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px">
|
||||
<a href="/" 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>
|
||||
<button id="themeBtn" title="toggle light / dark theme">☀</button>
|
||||
<button id="helpBtn" title="keyboard shortcuts (?)">?</button>
|
||||
<a class="brand" href="/"
|
||||
title="VARASYS PolyMeter — home" style="display:inline-flex; align-items:center; flex:0 0 auto">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kbd-legend" style="margin-bottom:12px"><span>Space play</span> <span>· T tap</span> <span>· ←→ tempo</span> <span>· ↑↓ cue</span> <span>· ⏎ commit</span> <span>· N/P step</span> <span>· A add</span> <span>· ? help</span></div>
|
||||
|
|
@ -373,6 +367,8 @@
|
|||
</aside>
|
||||
</div><!-- /#app -->
|
||||
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
|
||||
<!-- Subtle live program string for what's loaded: editable, copy & paste. -->
|
||||
<div class="patchbar" id="patchBar" title="Program string for what's loaded — edit it and press Enter (or paste one) to apply">
|
||||
<label for="patchField">program</label>
|
||||
|
|
@ -1194,18 +1190,7 @@ function syncStartBtn() {
|
|||
else { startBtn.textContent = "▶ Start"; startBtn.classList.remove("on"); }
|
||||
}
|
||||
function toggleShortcuts(show) { const o = $("shortcutsOverlay"); o.hidden = (show === undefined) ? !o.hidden : !show; }
|
||||
const THEMES = ["system", "light", "dark"];
|
||||
function effectiveTheme(pref) { return pref === "system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : pref; }
|
||||
function themePref() { try { const p = localStorage.getItem("metronome.theme"); return (p === "light" || p === "dark" || p === "system") ? p : "system"; } catch (e) { return "system"; } }
|
||||
function applyTheme(pref) {
|
||||
try { localStorage.setItem("metronome.theme", pref); } catch (e) {}
|
||||
document.documentElement.dataset.theme = effectiveTheme(pref);
|
||||
$("themeBtn").textContent = pref === "system" ? "◐" : pref === "light" ? "☀" : "☾"; // plain glyphs (emoji ones failed to render in some browsers)
|
||||
$("themeBtn").title = "Theme: " + pref + " (click to cycle: system → light → dark)";
|
||||
}
|
||||
$("themeBtn").addEventListener("click", () => applyTheme(THEMES[(THEMES.indexOf(themePref()) + 1) % THEMES.length]));
|
||||
matchMedia("(prefers-color-scheme: light)").addEventListener("change", () => { if (themePref() === "system") applyTheme("system"); });
|
||||
applyTheme(themePref());
|
||||
// theme toggle + version stamp are handled by the shared chrome (src/chrome.js), wired below.
|
||||
$("startBtn").addEventListener("click", () => toggleTransport());
|
||||
let _taps = [];
|
||||
function tapTempo() {
|
||||
|
|
@ -1343,8 +1328,8 @@ updateCtx();
|
|||
refreshFeatureBoxes();
|
||||
$("continueMode").checked = continueMode;
|
||||
$("timersOn").checked = timersOn;
|
||||
$("appVersion").textContent = "v" + APP_VERSION;
|
||||
requestAnimationFrame(drawLoop);
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
30
embed.html
30
embed.html
|
|
@ -38,21 +38,7 @@
|
|||
</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>PolyMeter</b> · Embed</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<span class="here">Embed</span>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
|
||||
<main>
|
||||
<h1>Embed a PolyMeter widget</h1>
|
||||
|
|
@ -97,22 +83,12 @@
|
|||
site chrome so only the widget shows. That's the same way our own Concept & Info pages embed it.</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());
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
</script>
|
||||
<script src="/embed.js"></script>
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
<!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="/editor.html">Open ↗</a>
|
||||
<a href="/">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="/editor.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-teacher.html">Teacher</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-teacher.html">Teacher</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>
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
<!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="/editor.html">Editor</a>
|
||||
<a href="/">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-teacher.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-teacher.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>
|
||||
118
info-micro.html
118
info-micro.html
|
|
@ -1,118 +0,0 @@
|
|||
<!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="/editor.html">Editor</a>
|
||||
<a href="/">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">Inline practice bar</span><span class="tag">~$35 one‑off</span></div>
|
||||
<p class="lead">A long, narrow practice bar you patch <i>into</i> your signal: instrument in one end, amp or
|
||||
headphones out the other, the click mixed in. One clickable thumb‑roller does everything, an amber
|
||||
14‑segment display shows tempo and track names, and it runs over USB‑C — a wall adapter or a power bank.</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="240"></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>Practising plugged in — at the desk or on the go. It sits inline in your signal chain: a 1/4″ TRS input on
|
||||
one end, USB‑C and a 1/4″ TRS output on the other, with the metronome click summed into your signal in the
|
||||
<b>analog domain</b> (and a small monitor speaker). No menus — a single clickable thumb‑roller does it all
|
||||
(roll = tempo, press = start/stop, hold + roll = switch track), and the amber 14‑segment display shows the
|
||||
BPM or the track name. Powered over USB‑C — a wall adapter for a permanent practice‑space install, or a
|
||||
pocket power bank when you're mobile (no internal battery to wear out); ships with the editor's grooves built in.</p>
|
||||
|
||||
<h2>Bill of materials</h2>
|
||||
<p class="sub">Rough parts list — a USB‑C‑powered RP2040 inline bar 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</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||
<tr><td class="part">4‑char 14‑segment alphanumeric LED + I²C driver <span class="spec">— amber; HT16K33. Shows BPM & track names</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||
<tr class="grp"><td colspan="3">Control</td></tr>
|
||||
<tr><td class="part">Clickable thumb‑roller <span class="spec">— EC11 encoder + roller wheel · roll / press / hold‑roll</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">PAM8302A mono Class‑D + 8 Ω 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)</span></td><td class="q">2</td><td class="c">2</td></tr>
|
||||
<tr><td class="part">USB‑C bus power (5 V) + PWR LED <span class="spec">— wall adapter or power bank; also carries config</span></td><td class="q">1</td><td class="c">1</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">4</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">2</td></tr>
|
||||
<tr><td class="part">Extruded aluminium bar enclosure + end caps <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">≈ $35</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="sub" style="margin-top:12px">Like the Teacher, the click is summed in the <b>analog domain</b>: a high‑impedance
|
||||
buffer of the 1/4″ instrument input is mixed with the DAC's click and sent to the 1/4″ output and the monitor
|
||||
amp — 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>
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PM‑S Showcase — 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; display:flex; justify-content:center; }
|
||||
.cap{ font-size:12px; color:var(--muted); text-align:center; }
|
||||
.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‑S</b> · Showcase — info</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<a href="/showcase.html">Open ↗</a>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h1>PM‑S — Showcase</h1>
|
||||
<div class="tags"><span class="tag hw">Hardware</span><span class="tag">Display piece</span><span class="tag">~$39 one‑off</span></div>
|
||||
<p class="lead">A metronome as an object: the silhouette of a classic pyramid wind‑up unit, but the swinging
|
||||
pendulum is pure <b>RGB light</b> — a glowing bob easing to the beat just like the mechanical original — with
|
||||
rows of light beneath it showing every lane's subdivisions, accents and mutes.</p>
|
||||
|
||||
<div class="embed-wrap">
|
||||
<div data-varasys-metronome="showcase"
|
||||
data-patch="v1;t108;kick:4=X..x;snare:4=.X.X;hatClosed:4/4;tom:3~" data-height="560"></div>
|
||||
</div>
|
||||
<p class="cap">Live widget (embedded). <a href="/showcase.html">Open the full Showcase page ↗</a> · <a href="/embed.html">embed this</a></p>
|
||||
|
||||
<h2>Designed for</h2>
|
||||
<p>The shelf, the studio, the shop window — a beautiful, glanceable tempo reference that's a pleasure to watch.
|
||||
The RGB pendulum swings in perfect time (decelerating to each extreme exactly as a weighted rod would), and
|
||||
the segment rows turn your polymeter pattern into a light show: accents glow amber, normal steps cyan, ghosts
|
||||
soft violet, mutes stay dark, and the playhead sweeps each lane. It runs the same grooves as everything else
|
||||
(load any program string), plays the click through a small speaker, and is powered over USB‑C with a second
|
||||
"thru" port to daisy‑chain. No instrument I/O — it's a showpiece, not a signal‑chain tool.</p>
|
||||
|
||||
<h2>Bill of materials</h2>
|
||||
<p class="sub">Rough parts list — a USB‑C‑powered RP2040 display piece driving addressable RGB light.
|
||||
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</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 class="grp"><td colspan="3">RGB light</td></tr>
|
||||
<tr><td class="part">Addressable RGB LEDs (WS2812B) <span class="spec">— pendulum rod + lane segment rows, ~50 px</span></td><td class="q">1</td><td class="c">5</td></tr>
|
||||
<tr><td class="part">Frosted acrylic diffuser / light‑guide <span class="spec">— the glowing "pendulum" face</span></td><td class="q">1</td><td class="c">3</td></tr>
|
||||
<tr class="grp"><td colspan="3">Audio</td></tr>
|
||||
<tr><td class="part">MAX98357A I²S amp + small speaker <span class="spec">— the click</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">2× USB‑C (data+power & power‑thru) + PWR LED <span class="spec">— daisy‑chain</span></td><td class="q">1</td><td class="c">3</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, wire</td><td class="q">—</td><td class="c">2</td></tr>
|
||||
<tr><td class="part">Pyramid enclosure <span class="spec">— cast/CNC aluminium or hardwood, frosted front panel</span></td><td class="q">1</td><td class="c">14</td></tr>
|
||||
<tr class="total"><td>Total (one‑off)</td><td class="q"></td><td class="c">≈ $39</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
121
info-stage.html
|
|
@ -1,121 +0,0 @@
|
|||
<!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="/editor.html">Editor</a>
|
||||
<a href="/">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">Foot‑pedal stompbox</span><span class="tag">~$52 one‑off</span></div>
|
||||
<p class="lead">A foot‑operated polymeter stompbox for the stage: drive it hands‑free with two heavy
|
||||
footswitches and an expression pedal, read it off the floor from a big RGB beat light, and run your
|
||||
instrument through it with the click mixed in. (For a desk/lesson unit with a full screen, see the
|
||||
<a href="/teacher.html">Teacher</a>.)</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="430"></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>Live use, hands‑free. The controls are built for feet: the <b>left footswitch</b> taps tempo (hold to
|
||||
start/stop), the <b>right</b> steps through your set list (hold for previous), and a <b>1/4″ expression‑pedal
|
||||
input</b> sweeps tempo on the fly. A large RGB <b>beat light</b> is readable from standing height, with a
|
||||
small angled TFT for the BPM, item name and beat. Your instrument passes through (1/4″ in) with the click
|
||||
summed in the <b>analog domain</b> and sent to a balanced 1/4″ TRS out for the desk. Powered over USB‑C —
|
||||
with a <b>second USB‑C "thru" port</b> so several pedals daisy‑chain off one charger or power bank.</p>
|
||||
|
||||
<h2>Bill of materials</h2>
|
||||
<p class="sub">Rough parts list — a foot‑operated RP2040 stompbox (USB‑C, dual‑port) 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</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||
<tr><td class="part">1.3″ IPS TFT, ST7789 <span class="spec">— SPI; angled BPM / item readout</span></td><td class="q">1</td><td class="c">6</td></tr>
|
||||
<tr><td class="part">High‑bright diffused RGB beat indicator <span class="spec">— floor‑readable</span></td><td class="q">1</td><td class="c">1</td></tr>
|
||||
<tr class="grp"><td colspan="3">Controls</td></tr>
|
||||
<tr><td class="part">Heavy‑duty momentary footswitch (soft‑touch) <span class="spec">— Tap · Next</span></td><td class="q">2</td><td class="c">6</td></tr>
|
||||
<tr><td class="part">1/4″ expression‑pedal input jack (TRS) <span class="spec">— tempo sweep</span></td><td class="q">1</td><td class="c">1</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">— → 1/4″ TRS out</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">2× USB‑C (data+power & power‑thru) + power‑path/protection + PWR LED <span class="spec">— daisy‑chain pedals</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 stompbox (Hammond 1590BB‑style) <span class="spec">— bead‑blasted, matte‑black Type II anodise, laser‑etched</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">≈ $52</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="sub" style="margin-top:12px">No built‑in speaker — the Stage feeds your amp / PA. The click is summed in the
|
||||
<b>analog domain</b> (hi‑Z instrument buffer + DAC → balanced line driver), 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>
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PM‑1 Teacher — 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> · Teacher — info</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<a href="/teacher.html">Open ↗</a>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h1>PM‑1 — Teacher</h1>
|
||||
<div class="tags"><span class="tag hw">Hardware</span><span class="tag">Studio / lesson console</span><span class="tag">~$59 one‑off</span></div>
|
||||
<p class="lead">The full‑feature desktop console: a colour readout of every lane, fast set‑list navigation, and
|
||||
your instrument running straight through with the click mixed in — the hands‑on unit for a studio desk or a
|
||||
teaching room, on a non‑reflective matte‑black case. (For hands‑free live use, see the foot‑operated
|
||||
<a href="/stage.html">Stage</a> stompbox.)</p>
|
||||
|
||||
<div class="embed-wrap">
|
||||
<div data-varasys-metronome="teacher"
|
||||
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="/teacher.html">Open the full Teacher page ↗</a> · <a href="/embed.html">embed this</a></p>
|
||||
|
||||
<h2>Designed for</h2>
|
||||
<p>The studio desk and the teaching room. Top‑mounted 1/4″ jacks keep cabling tidy; 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 or interface, plus a small monitor
|
||||
speaker. Powered over USB‑C — a wall adapter, or a power bank. The colour TFT shows tempo, the item name,
|
||||
and all lane patterns; arcade buttons + a recessed thumb‑roller make it quick to drive while you teach or track.</p>
|
||||
|
||||
<h2>Bill of materials</h2>
|
||||
<p class="sub">Rough parts list — a desk/studio RP2040 build (USB‑C powered) 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">— wall adapter or power bank; same port carries config; no battery</span></td><td class="q">1</td><td class="c">1</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">≈ $56</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>
|
||||
70
micro.html
70
micro.html
|
|
@ -125,22 +125,7 @@
|
|||
</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 (inline practice bar)</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<a href="/info-micro.html">Info</a>
|
||||
<a href="/embed.html">Embed</a>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
|
||||
<div class="device">
|
||||
<!-- LEFT END: instrument / aux in -->
|
||||
|
|
@ -309,15 +294,7 @@ function commitTrack(){ loadTrack(previewIdx); displayMode="track"; render(); cl
|
|||
})();
|
||||
|
||||
/* theme toggle — cycles system → light → dark; shares the "metronome.theme" key */
|
||||
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+" (system → light → dark)"; }
|
||||
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||
applyTheme(themePref());
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
|
||||
addEventListener("keydown",(e)=>{
|
||||
const tag=e.target?e.target.tagName:""; if(tag==="INPUT"||tag==="TEXTAREA"||tag==="SELECT") return;
|
||||
|
|
@ -334,5 +311,48 @@ addEventListener("keydown",(e)=>{
|
|||
loadTrack(0);
|
||||
render();
|
||||
</script>
|
||||
|
||||
<section class="about pageonly">
|
||||
<h2>PM‑µ — Micro</h2>
|
||||
<div class="ff-tags"><span class="hw">Hardware</span><span>Inline practice bar</span><span>~$35 one‑off</span></div>
|
||||
<p>A long, narrow practice bar you patch <i>into</i> your signal: instrument in one end, amp or headphones out
|
||||
the other, the click mixed in. One clickable thumb‑roller does everything (roll = tempo, press = start/stop,
|
||||
hold + roll = switch track), and an amber 14‑segment display shows tempo and track names.</p>
|
||||
<p>The click is summed into your signal in the <b>analog domain</b> (plus a small monitor speaker). Powered over
|
||||
USB‑C — a wall adapter for a permanent practice‑space install, or a pocket power bank when you're mobile (no
|
||||
internal battery to wear out); ships with the editor's grooves built in.</p>
|
||||
</section>
|
||||
|
||||
<details class="spec pageonly">
|
||||
<summary>Spec & bill of materials</summary>
|
||||
<div class="spec-body">
|
||||
<p class="sub">Rough parts list — a USB‑C‑powered RP2040 inline bar 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</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||
<tr><td class="part">4‑char 14‑segment alphanumeric LED + I²C driver <span class="spec">— amber; HT16K33. Shows BPM & track names</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||
<tr class="grp"><td colspan="3">Control</td></tr>
|
||||
<tr><td class="part">Clickable thumb‑roller <span class="spec">— EC11 encoder + roller wheel · roll / press / hold‑roll</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">PAM8302A mono Class‑D + 8 Ω 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)</span></td><td class="q">2</td><td class="c">2</td></tr>
|
||||
<tr><td class="part">USB‑C bus power (5 V) + PWR LED <span class="spec">— wall adapter or power bank; also carries config</span></td><td class="q">1</td><td class="c">1</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">4</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">2</td></tr>
|
||||
<tr><td class="part">Extruded aluminium bar enclosure + end caps <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">≈ $35</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
58
player.html
58
player.html
|
|
@ -198,29 +198,21 @@
|
|||
background:var(--bg1); color:var(--txt); font-size:20px; text-align:center; padding:24px }
|
||||
body.stage .rotate-hint .rh-icon{ font-size:64px; line-height:1; color:var(--cyan) }
|
||||
}
|
||||
/* fullscreen toggle (relocated out of the shared header) */
|
||||
.fs-float{ background:transparent; color:var(--muted); border:1px solid var(--panel-bd); border-radius:8px;
|
||||
padding:4px 11px; font-size:15px; line-height:1; cursor:pointer; }
|
||||
.fs-float:hover{ color:var(--txt); }
|
||||
body.stage .fs-float{ display:none; }
|
||||
/* embed mode: just the device */
|
||||
[data-embed] .panel { display:none !important; }
|
||||
[data-embed] .panel, [data-embed] .fs-float { display:none !important; }
|
||||
</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 concept</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<a href="/info-initial.html">Info</a>
|
||||
<a href="/embed.html">Embed</a>
|
||||
<button id="fsBtn" class="tbtn" title="Full screen (landscape)">⛶</button>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
|
||||
<!-- fullscreen "stage mode" toggle — floats over the page (was a header button before the shared header) -->
|
||||
<button id="fsBtn" class="fs-float" title="Full screen (landscape)">⛶</button>
|
||||
|
||||
<!-- ===================== THE DEVICE ===================== -->
|
||||
<div class="device">
|
||||
|
|
@ -442,21 +434,7 @@ $("storedSel").onchange=(e)=>{ const v=e.target.value; if(!v) return;
|
|||
const sl = v[0]==="b" ? e.target._builtin[+v.slice(1)] : e.target._lists[+v.slice(1)]; if(!sl) return;
|
||||
loadSetlistObj({title:sl.title,items:(sl.items||[]).map(it=>({...it}))}); setStatus("✓ Loaded set list “"+(sl.title||"set list")+"”.",true); };
|
||||
/* theme toggle — cycles system → light → dark; shares the editor's "metronome.theme" key */
|
||||
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);
|
||||
const glyph = p==="system" ? "◐" : p==="light" ? "☀" : "☾";
|
||||
const title = "Theme: "+p+" (click to cycle: system → light → dark)";
|
||||
for(const id of ["themeBtn","fsThemeBtn"]){ const b=$(id); if(b){ b.textContent=glyph; b.title=title; } }
|
||||
}
|
||||
const cycleTheme = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||
$("themeBtn").onclick = cycleTheme;
|
||||
$("fsThemeBtn").onclick = cycleTheme;
|
||||
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||
applyTheme(themePref());
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
|
||||
/* full-screen "stage" mode — real fullscreen + landscape lock where supported (Android/desktop),
|
||||
CSS pseudo-fullscreen + a rotate hint where not (iPhone). body.stage drives the layout. */
|
||||
|
|
@ -510,5 +488,19 @@ if(!setlist){ setlist=BUILTIN[0]; idx=0; loadSetup(setlist.items[0]); }
|
|||
renderAll();
|
||||
requestAnimationFrame(draw);
|
||||
</script>
|
||||
<section class="about pageonly">
|
||||
<h2>PM‑1 — Initial</h2>
|
||||
<div class="ff-tags"><span>Concept</span><span>Idealized device</span><span>Not buildable as drawn</span></div>
|
||||
<p>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> — 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>
|
||||
<p>Because it's a concept, 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="/teacher.html">PM‑1 Teacher</a> (full priced BOM there);
|
||||
for the smallest practical unit, see the <a href="/micro.html">PM‑µ Micro</a>.</p>
|
||||
</section>
|
||||
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,47 @@
|
|||
<div class="hint">The pendulum <b>is</b> the display: every lane's subdivisions & accents ride along the bar as
|
||||
moving RGB light. Drag the <b>weight</b> up/down (or scroll) to set tempo — the scale is printed on the bar,
|
||||
just like a wind‑up metronome. (No power switch: the real one starts when you lift it from its holder.)</div>
|
||||
|
||||
<section class="about pageonly">
|
||||
<h2>PM‑S — Showcase</h2>
|
||||
<div class="ff-tags"><span class="hw">Hardware</span><span>Display piece</span><span>~$41 one‑off</span></div>
|
||||
<p>A metronome as an object: the silhouette of a classic pyramid wind‑up unit, but the swinging pendulum is
|
||||
pure <b>RGB light</b>. The whole bar is the display — every lane's subdivisions & accents ride along its
|
||||
length as moving points of light (all meters combined), a printed tempo scale runs up the vertical axis,
|
||||
and a sliding <b>weight</b> sets the tempo just like the mechanical original.</p>
|
||||
<p>It's a beautiful, glanceable tempo reference for the shelf, the studio, or a shop window: accents glow
|
||||
amber, normal steps cyan, ghosts soft violet, and the pendulum eases to each beat exactly as a weighted rod
|
||||
would. It runs the same grooves as everything else (load any program string), plays the click through a
|
||||
small speaker, and is powered over USB‑C with a second "thru" port to daisy‑chain. There's no power switch —
|
||||
the real unit starts when you lift it from its holder / set it swinging. No instrument I/O; it's a showpiece.</p>
|
||||
</section>
|
||||
|
||||
<details class="spec pageonly">
|
||||
<summary>Spec & bill of materials</summary>
|
||||
<div class="spec-body">
|
||||
<p class="sub">Rough parts list — a USB‑C‑powered RP2040 display piece driving addressable RGB light.
|
||||
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</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 class="grp"><td colspan="3">RGB light</td></tr>
|
||||
<tr><td class="part">Addressable RGB LEDs (WS2812B) <span class="spec">— a strip down the pendulum bar, ~40 px</span></td><td class="q">1</td><td class="c">5</td></tr>
|
||||
<tr><td class="part">Frosted acrylic diffuser / light‑guide <span class="spec">— the glowing pendulum bar</span></td><td class="q">1</td><td class="c">3</td></tr>
|
||||
<tr class="grp"><td colspan="3">Audio</td></tr>
|
||||
<tr><td class="part">MAX98357A I²S amp + small speaker <span class="spec">— the click</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">2× USB‑C (data+power & power‑thru) + PWR LED <span class="spec">— daisy‑chain</span></td><td class="q">1</td><td class="c">3</td></tr>
|
||||
<tr><td class="part">Tilt / lift sensor (accelerometer) <span class="spec">— starts when lifted from its holder</span></td><td class="q">1</td><td class="c">2</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, wire</td><td class="q">—</td><td class="c">2</td></tr>
|
||||
<tr><td class="part">Pyramid enclosure <span class="spec">— cast/CNC aluminium or hardwood, frosted front panel</span></td><td class="q">1</td><td class="c">14</td></tr>
|
||||
<tr class="total"><td>Total (one‑off)</td><td class="q"></td><td class="c">≈ $41</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
</main>
|
||||
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
|
|
|
|||
10
src/base.css
10
src/base.css
|
|
@ -63,3 +63,13 @@ details.spec > summary::before { content:"▸"; color:var(--muted,#7f8b9a); tran
|
|||
details.spec[open] > summary::before { transform:rotate(90deg); }
|
||||
details.spec .spec-body { padding:2px 16px 16px; }
|
||||
[data-embed] details.spec { display:none !important; }
|
||||
|
||||
/* ---- per-form-factor page: the description above the expandable spec ---- */
|
||||
.about { width:100%; max-width:760px; margin:20px auto 0; color:var(--muted,#7f8b9a); font-size:14px; line-height:1.62; }
|
||||
.about h2 { color:var(--txt,#c7d0db); font-size:18px; margin:0 0 4px; }
|
||||
.about .ff-tags { display:flex; gap:8px; flex-wrap:wrap; margin:6px 0 10px; }
|
||||
.about .ff-tags span { font-size:11px; color:var(--muted,#7f8b9a); border:1px solid var(--panel-bd,#2a313c); border-radius:999px; padding:2px 9px; }
|
||||
.about .ff-tags .hw { color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||
.about p { margin:0 0 10px; max-width:64ch; }
|
||||
/* page-only chrome (description, dimensioned views, loading panel) — hidden when embedded */
|
||||
[data-embed] .pageonly { display:none !important; }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
the page (deploy.sh rewrites that line) and the header/footer markup present. */
|
||||
(function () {
|
||||
var byId = function (id) { return document.getElementById(id); };
|
||||
try { var v = byId("appVersion"); if (v && typeof APP_VERSION !== "undefined") v.textContent = "v" + APP_VERSION.replace(/^v/, ""); } catch (e) {}
|
||||
var THEMES = ["system", "light", "dark"];
|
||||
function eff(p) { return p === "system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p; }
|
||||
function pref() { try { var p = localStorage.getItem("metronome.theme"); return (p === "light" || p === "dark" || p === "system") ? p : "system"; } catch (e) { return "system"; } }
|
||||
|
|
@ -13,7 +12,11 @@
|
|||
document.documentElement.dataset.theme = eff(p);
|
||||
var b = byId("themeBtn"); if (b) { b.textContent = p === "system" ? "◐" : p === "light" ? "☀" : "☾"; b.title = "Theme: " + p + " (system → light → dark)"; }
|
||||
}
|
||||
function init() {
|
||||
try { var v = byId("appVersion"); if (v && typeof APP_VERSION !== "undefined") v.textContent = "v" + APP_VERSION.replace(/^v/, ""); } catch (e) {}
|
||||
var btn = byId("themeBtn"); if (btn) btn.onclick = function () { apply(THEMES[(THEMES.indexOf(pref()) + 1) % THEMES.length]); };
|
||||
try { matchMedia("(prefers-color-scheme: light)").addEventListener("change", function () { if (pref() === "system") apply("system"); }); } catch (e) {}
|
||||
apply(pref());
|
||||
}
|
||||
try { matchMedia("(prefers-color-scheme: light)").addEventListener("change", function () { if (pref() === "system") apply("system"); }); } catch (e) {}
|
||||
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); else init();
|
||||
})();
|
||||
|
|
|
|||
77
stage.html
77
stage.html
|
|
@ -109,22 +109,7 @@
|
|||
</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 (foot‑pedal stompbox)</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<a href="/info-stage.html">Info</a>
|
||||
<a href="/embed.html">Embed</a>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
|
||||
<div class="device">
|
||||
<!-- top edge: all jacks, including dual USB-C daisy-chain power -->
|
||||
|
|
@ -297,15 +282,7 @@ holdSwitch($("swNext"), ()=>loadTrack(trackIdx+1), ()=>loadTrack(trackIdx-1));
|
|||
$("expPedal").addEventListener("input", (e)=>{ setBpm(+e.target.value); });
|
||||
|
||||
/* theme toggle (shared "metronome.theme") */
|
||||
const THEMES=["system","light","dark"];
|
||||
function effTheme(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 = effTheme(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());
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
|
||||
addEventListener("keydown",(e)=>{
|
||||
const tag=e.target?e.target.tagName:""; if(tag==="INPUT"||tag==="TEXTAREA"||tag==="SELECT") return;
|
||||
|
|
@ -320,5 +297,55 @@ addEventListener("keydown",(e)=>{
|
|||
loadTrack(0);
|
||||
requestAnimationFrame(draw);
|
||||
</script>
|
||||
|
||||
<section class="about pageonly">
|
||||
<h2>PM‑1 — Stage</h2>
|
||||
<div class="ff-tags"><span class="hw">Hardware</span><span>Foot‑pedal stompbox</span><span>~$52 one‑off</span></div>
|
||||
<p>A foot‑operated polymeter stompbox for the stage: drive it hands‑free with two heavy footswitches and an
|
||||
expression pedal, read it off the floor from the big RGB beat light, and run your instrument through it with
|
||||
the click mixed in. (For a desk/lesson unit with a full screen, see the <a href="/teacher.html">Teacher</a>.)</p>
|
||||
<p>The controls are built for feet: the <b>left footswitch</b> taps tempo (hold to start/stop), the <b>right</b>
|
||||
steps through your set list (hold for previous), and a <b>1/4″ expression‑pedal input</b> sweeps tempo on the
|
||||
fly. Your instrument passes through (1/4″ in) with the click summed in the <b>analog domain</b> and sent to a
|
||||
balanced 1/4″ TRS out. Powered over USB‑C — with a second USB‑C <b>"thru"</b> port so several pedals
|
||||
daisy‑chain off one charger or power bank.</p>
|
||||
</section>
|
||||
|
||||
<details class="spec pageonly">
|
||||
<summary>Spec & bill of materials</summary>
|
||||
<div class="spec-body">
|
||||
<p class="sub">Rough parts list — a foot‑operated RP2040 stompbox (USB‑C, dual‑port) 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</span></td><td class="q">1</td><td class="c">4</td></tr>
|
||||
<tr><td class="part">1.3″ IPS TFT, ST7789 <span class="spec">— SPI; angled BPM / item readout</span></td><td class="q">1</td><td class="c">6</td></tr>
|
||||
<tr><td class="part">High‑bright diffused RGB beat indicator <span class="spec">— floor‑readable</span></td><td class="q">1</td><td class="c">1</td></tr>
|
||||
<tr class="grp"><td colspan="3">Controls</td></tr>
|
||||
<tr><td class="part">Heavy‑duty momentary footswitch (soft‑touch) <span class="spec">— Tap · Next</span></td><td class="q">2</td><td class="c">6</td></tr>
|
||||
<tr><td class="part">1/4″ expression‑pedal input jack (TRS) <span class="spec">— tempo sweep</span></td><td class="q">1</td><td class="c">1</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">— → 1/4″ TRS out</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">2× USB‑C (data+power & power‑thru) + power‑path/protection + PWR LED <span class="spec">— daisy‑chain pedals</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 stompbox (Hammond 1590BB‑style) <span class="spec">— bead‑blasted, matte‑black Type II anodise, laser‑etched</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">≈ $52</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="sub" style="margin-top:12px">No built‑in speaker — the Stage feeds your amp / PA. The click is summed in
|
||||
the <b>analog domain</b> (hi‑Z instrument buffer + DAC → balanced line driver), so your instrument is never
|
||||
re‑digitised (no added latency).</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
82
teacher.html
82
teacher.html
|
|
@ -202,22 +202,7 @@
|
|||
</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> · Teacher (studio / lesson console)</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/editor.html">Editor</a>
|
||||
<a href="/">Concepts</a>
|
||||
<a href="/info-teacher.html">Info</a>
|
||||
<a href="/embed.html">Embed</a>
|
||||
<button id="themeBtn" class="tbtn" title="toggle light / dark theme">☀</button>
|
||||
</nav>
|
||||
</header>
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
|
||||
<div class="cols">
|
||||
<div class="col-left">
|
||||
|
|
@ -287,10 +272,59 @@
|
|||
<div class="status" id="status"></div>
|
||||
</div>
|
||||
|
||||
<p class="speclink"><a href="/info-teacher.html">Spec & full priced BOM ⓘ</a> — what the Teacher is designed for, and every part to build one.</p>
|
||||
</div><!-- /col-left -->
|
||||
</div><!-- /cols -->
|
||||
|
||||
<section class="about pageonly">
|
||||
<h2>PM‑1 — Teacher</h2>
|
||||
<div class="ff-tags"><span class="hw">Hardware</span><span>Studio / lesson console</span><span>~$59 one‑off</span></div>
|
||||
<p>The full‑feature desktop console: a colour readout of every lane, fast set‑list navigation, and your
|
||||
instrument running straight through with the click mixed in — the hands‑on unit for a studio desk or a
|
||||
teaching room, on a non‑reflective matte‑black case. (For hands‑free live use, see the foot‑operated
|
||||
<a href="/stage.html">Stage</a> stompbox.)</p>
|
||||
<p>Top‑mounted 1/4″ jacks keep cabling tidy; 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 or interface, plus a small monitor speaker. Powered over USB‑C — a wall adapter or a power bank. The
|
||||
colour TFT shows tempo, the item name and all lane patterns; arcade buttons + a recessed thumb‑roller make
|
||||
it quick to drive while you teach or track.</p>
|
||||
</section>
|
||||
|
||||
<details class="spec pageonly">
|
||||
<summary>Spec & bill of materials</summary>
|
||||
<div class="spec-body">
|
||||
<p class="sub">Rough parts list — a desk/studio RP2040 build (USB‑C powered) 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">— wall adapter or power bank; same port carries config; no battery</span></td><td class="q">1</td><td class="c">1</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">≈ $56</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>
|
||||
</details>
|
||||
|
||||
<script>
|
||||
const APP_VERSION = "v0.0.1-dev";
|
||||
const $ = (id) => document.getElementById(id);
|
||||
|
|
@ -500,18 +534,7 @@ $("storedSel").onchange=(e)=>{ const v=e.target.value; if(!v) return;
|
|||
})();
|
||||
|
||||
/* theme toggle — cycles system → light → dark; shares the editor's "metronome.theme" key */
|
||||
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+" (click to cycle: system → light → dark)";
|
||||
}
|
||||
$("themeBtn").onclick = ()=> applyTheme(THEMES[(THEMES.indexOf(themePref())+1)%THEMES.length]);
|
||||
matchMedia("(prefers-color-scheme: light)").addEventListener("change", ()=>{ if(themePref()==="system") applyTheme("system"); });
|
||||
applyTheme(themePref());
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
|
||||
addEventListener("keydown",(e)=>{
|
||||
const t=e.target, tag=t?t.tagName:""; if(tag==="TEXTAREA"||tag==="INPUT"||tag==="SELECT") return;
|
||||
|
|
@ -531,5 +554,6 @@ if(!setlist){ setlist=BUILTIN[0]; idx=0; loadSetup(setlist.items[0]); }
|
|||
renderAll();
|
||||
requestAnimationFrame(draw);
|
||||
</script>
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue