Compare commits
No commits in common. "678482a0f24b3d8ce3ea447ab157884f5a4c74ad" and "e048be390f14532c8a35e09a3e1c4d9419d93674" have entirely different histories.
678482a0f2
...
e048be390f
19 changed files with 842 additions and 257 deletions
17
README.md
17
README.md
|
|
@ -26,14 +26,11 @@ 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; the pendulum is an RGB light bar combining every lane's subdivisions/accents |
|
||||
| `/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) |
|
||||
| `/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)).
|
||||
|
|
@ -161,9 +158,8 @@ 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. The
|
||||
[Concepts landing](index.html) dogfoods this exact mechanism (every box is a live widget).
|
||||
See `/embed.html`.
|
||||
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`.
|
||||
|
||||
## Keyboard shortcuts
|
||||
|
||||
|
|
@ -215,7 +211,7 @@ in sync:
|
|||
assets from `assets/`.
|
||||
|
||||
`./build.sh` resolves every marker into a self‑contained page in `dist/` — the editor +
|
||||
the Concepts landing + editor + device/form-factor pages — and copies `embed.js` through as‑is
|
||||
concepts + device mockups + the `info-*.html` 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.
|
||||
|
|
@ -239,6 +235,7 @@ 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) |
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.0.40
|
||||
0.0.39
|
||||
|
|
|
|||
3
build.sh
3
build.sh
|
|
@ -32,7 +32,8 @@ 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"):
|
||||
"embed.html",
|
||||
"info-editor.html","info-initial.html","info-teacher.html","info-stage.html","info-micro.html","info-showcase.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; do
|
||||
embed.html \
|
||||
info-editor.html info-initial.html info-teacher.html info-stage.html info-micro.html info-showcase.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,15 +237,21 @@
|
|||
</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></h1>
|
||||
<h1 style="margin:0">PE‑1 <span style="font-weight:400; opacity:.75">PolyMeter Editor</span> <span class="lane-meta" id="appVersion" title="build version">v0.0.1-dev</span></h1>
|
||||
<div class="appheader-ctrls" style="display:flex; align-items:center; gap:10px">
|
||||
<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>
|
||||
|
|
@ -367,8 +373,6 @@
|
|||
</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>
|
||||
|
|
@ -1190,7 +1194,18 @@ function syncStartBtn() {
|
|||
else { startBtn.textContent = "▶ Start"; startBtn.classList.remove("on"); }
|
||||
}
|
||||
function toggleShortcuts(show) { const o = $("shortcutsOverlay"); o.hidden = (show === undefined) ? !o.hidden : !show; }
|
||||
// theme toggle + version stamp are handled by the shared chrome (src/chrome.js), wired below.
|
||||
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());
|
||||
$("startBtn").addEventListener("click", () => toggleTransport());
|
||||
let _taps = [];
|
||||
function tapTempo() {
|
||||
|
|
@ -1328,8 +1343,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,7 +38,21 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
<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>
|
||||
|
||||
<main>
|
||||
<h1>Embed a PolyMeter widget</h1>
|
||||
|
|
@ -83,12 +97,22 @@
|
|||
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);
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
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>
|
||||
/*@BUILD:include:src/footer.html@*/
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
99
info-editor.html
Normal file
99
info-editor.html
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PE‑1 PolyMeter Editor — info — VARASYS</title>
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||
<script>
|
||||
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||
</script>
|
||||
<style>
|
||||
/*@BUILD:include:src/base.css@*/
|
||||
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||
a{ color:var(--link); }
|
||||
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||
.tag.app{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||
ul{ color:var(--muted); font-size:14px; line-height:1.7; padding-left:20px; } ul b{ color:var(--txt); }
|
||||
.embed-wrap{ margin:16px 0 4px; }
|
||||
.cap{ font-size:12px; color:var(--muted); }
|
||||
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-head">
|
||||
<div class="head-left">
|
||||
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||
</a>
|
||||
<span class="page-name"><b>PE‑1</b> · PolyMeter Editor — info</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/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>
|
||||
95
info-initial.html
Normal file
95
info-initial.html
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PM‑1 Initial — info — VARASYS</title>
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||
<script>
|
||||
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||
</script>
|
||||
<style>
|
||||
/*@BUILD:include:src/base.css@*/
|
||||
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||
a{ color:var(--link); }
|
||||
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||
.tag.concept{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||
.embed-wrap{ margin:16px 0 4px; }
|
||||
.cap{ font-size:12px; color:var(--muted); }
|
||||
.note{ margin-top:18px; padding:12px 14px; border:1px solid var(--panel-bd); border-radius:10px; background:var(--panel-bg); }
|
||||
.note p{ margin:0; }
|
||||
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-head">
|
||||
<div class="head-left">
|
||||
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||
</a>
|
||||
<span class="page-name"><b>PM‑1</b> · Initial — info</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/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
Normal file
118
info-micro.html
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<!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>
|
||||
111
info-showcase.html
Normal file
111
info-showcase.html
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<!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
Normal file
121
info-stage.html
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PM‑1 Stage — info & BOM — VARASYS</title>
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,@BUILD:favicon@">
|
||||
<script>
|
||||
(function(){ try{ var p = localStorage.getItem("metronome.theme");
|
||||
if (p!=="light" && p!=="dark" && p!=="system") p = "system";
|
||||
document.documentElement.dataset.theme = p==="system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : p;
|
||||
} catch(e){ document.documentElement.dataset.theme = "dark"; } })();
|
||||
</script>
|
||||
<style>
|
||||
/*@BUILD:include:src/base.css@*/
|
||||
:root{ --bg1:#12151c;--bg2:#05070a;--txt:#c7d0db;--muted:#7f8b9a;--link:#6cb6ff;--panel-bg:#161b22;--panel-bd:#2a313c;--field-bg:#0e1116; }
|
||||
:root[data-theme="light"]{ --bg1:#f5f8fc;--bg2:#dde4ec;--txt:#1e2630;--muted:#5c6776;--link:#1769c4;--panel-bg:#fff;--panel-bd:#d2dae4;--field-bg:#f1f4f8; }
|
||||
body{ margin:0; min-height:100vh; padding:22px 16px 56px; color:var(--txt); background:radial-gradient(circle at 50% -8%,var(--bg1),var(--bg2)); }
|
||||
a{ color:var(--link); }
|
||||
main{ width:100%; max-width:760px; margin:26px auto 0; }
|
||||
h1{ font-size:24px; margin:0 0 4px; } h2{ font-size:16px; margin:28px 0 8px; }
|
||||
p{ color:var(--muted); font-size:14px; line-height:1.6; } p.lead{ max-width:62ch; color:var(--txt); }
|
||||
.tags{ display:flex; gap:8px; flex-wrap:wrap; margin:8px 0 4px; }
|
||||
.tag{ font-size:11px; color:var(--muted); border:1px solid var(--panel-bd); border-radius:999px; padding:2px 9px; }
|
||||
.tag.hw{ color:var(--cyan); border-color:rgba(10,179,247,.45); }
|
||||
.embed-wrap{ margin:16px 0 4px; }
|
||||
.cap{ font-size:12px; color:var(--muted); }
|
||||
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="site-head">
|
||||
<div class="head-left">
|
||||
<a class="brand" href="/" title="VARASYS — Simplifying Complexity">
|
||||
<img class="brand-logo brand-dark" src="data:image/png;base64,@BUILD:logo-dark@" alt="VARASYS — Simplifying Complexity" />
|
||||
<img class="brand-logo brand-light" src="data:image/png;base64,@BUILD:logo-light@" alt="VARASYS — Simplifying Complexity" />
|
||||
</a>
|
||||
<span class="page-name"><b>PM‑1</b> · Stage — info</span>
|
||||
</div>
|
||||
<nav class="site-nav">
|
||||
<a href="/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>
|
||||
121
info-teacher.html
Normal file
121
info-teacher.html
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PM‑1 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,7 +125,22 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
<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>
|
||||
|
||||
<div class="device">
|
||||
<!-- LEFT END: instrument / aux in -->
|
||||
|
|
@ -294,7 +309,15 @@ function commitTrack(){ loadTrack(previewIdx); displayMode="track"; render(); cl
|
|||
})();
|
||||
|
||||
/* theme toggle — cycles system → light → dark; shares the "metronome.theme" key */
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
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());
|
||||
|
||||
addEventListener("keydown",(e)=>{
|
||||
const tag=e.target?e.target.tagName:""; if(tag==="INPUT"||tag==="TEXTAREA"||tag==="SELECT") return;
|
||||
|
|
@ -311,48 +334,5 @@ 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,21 +198,29 @@
|
|||
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, [data-embed] .fs-float { display:none !important; }
|
||||
[data-embed] .panel { display:none !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
/*@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>
|
||||
<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>
|
||||
|
||||
<!-- ===================== THE DEVICE ===================== -->
|
||||
<div class="device">
|
||||
|
|
@ -434,7 +442,21 @@ $("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 */
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
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());
|
||||
|
||||
/* 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. */
|
||||
|
|
@ -488,19 +510,5 @@ 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,47 +69,6 @@
|
|||
<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,13 +63,3 @@ 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,6 +4,7 @@
|
|||
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"; } }
|
||||
|
|
@ -12,11 +13,7 @@
|
|||
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]); };
|
||||
apply(pref());
|
||||
}
|
||||
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) {}
|
||||
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); else init();
|
||||
apply(pref());
|
||||
})();
|
||||
|
|
|
|||
77
stage.html
77
stage.html
|
|
@ -109,7 +109,22 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
<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>
|
||||
|
||||
<div class="device">
|
||||
<!-- top edge: all jacks, including dual USB-C daisy-chain power -->
|
||||
|
|
@ -282,7 +297,15 @@ holdSwitch($("swNext"), ()=>loadTrack(trackIdx+1), ()=>loadTrack(trackIdx-1));
|
|||
$("expPedal").addEventListener("input", (e)=>{ setBpm(+e.target.value); });
|
||||
|
||||
/* theme toggle (shared "metronome.theme") */
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
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());
|
||||
|
||||
addEventListener("keydown",(e)=>{
|
||||
const tag=e.target?e.target.tagName:""; if(tag==="INPUT"||tag==="TEXTAREA"||tag==="SELECT") return;
|
||||
|
|
@ -297,55 +320,5 @@ 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,7 +202,22 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
/*@BUILD:include:src/header.html@*/
|
||||
<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>
|
||||
|
||||
<div class="cols">
|
||||
<div class="col-left">
|
||||
|
|
@ -272,59 +287,10 @@
|
|||
<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);
|
||||
|
|
@ -534,7 +500,18 @@ $("storedSel").onchange=(e)=>{ const v=e.target.value; if(!v) return;
|
|||
})();
|
||||
|
||||
/* theme toggle — cycles system → light → dark; shares the editor's "metronome.theme" key */
|
||||
/*@BUILD:include:src/chrome.js@*/
|
||||
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());
|
||||
|
||||
addEventListener("keydown",(e)=>{
|
||||
const t=e.target, tag=t?t.tagName:""; if(tag==="TEXTAREA"||tag==="INPUT"||tag==="SELECT") return;
|
||||
|
|
@ -554,6 +531,5 @@ 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