metronome/embed.js
Me Here 17053719f1 New Stage (foot-pedal) + Showcase (RGB pendulum); fix audio/visual sync
Sync: the visual playhead now advances on a latency-compensated clock
(currentTime − outputLatency||baseLatency) so the on-screen pulse lands when the
click is HEARD, not when it's queued — previously the visual could lead the audio
by the output buffer / Bluetooth latency (up to ~a subdivision). Applied to
editor, player, teacher, and the new pages; also bound the visual queue (vq trim).
No data races: single-threaded; only the rAF draw touches vqPtr/currentStep, and
each vq entry carries the exact scheduled time of its sound.

stage.html — foot-pedal stompbox: two heavy footswitches (Tap=tempo / hold=start-
stop, Next=item / hold=prev), 1/4" expression-pedal input → tempo sweep, big
floor-readable RGB beat light + angled TFT, analog instrument pass-through.
showcase.html — pyramid display piece: an RGB-light pendulum easing to each beat
plus per-lane segment rows showing subdivisions/accents/mutes (canvas).
Both: dual USB-C (data+power and power-thru) to daisy-chain off one source.

Wired into embed.js (stage, showcase variants), build.sh, deploy.sh, the
concepts gallery + landing cards, info-stage.html (~$52) + info-showcase.html
(~$39) with BOMs, and the README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 08:40:20 -05:00

55 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* VARASYS PolyMeter — embed loader (zero-dependency, ~1 KB).
*
* Drop a placeholder + this script into any page:
* <div data-varasys-metronome="micro" data-patch="v1;t120;kick:4;snare:4=.X.X"></div>
* <script src="https://metronome.varasys.io/embed.js"></script>
*
* Attributes:
* data-varasys-metronome editor | initial | teacher | stage | micro | showcase (which form factor)
* data-patch a PolyMeter program/settings string (preloads it)
* data-setlist a base64url set-list code (alternative to data-patch)
* data-width / data-height iframe size (default 100% × 300; height auto-grows)
*
* Each placeholder becomes an <iframe src=".../<page>?embed=1#p=<patch>"> (the page's
* own ?embed=1 mode strips the site chrome) and auto-resizes to the widget's content.
*/
(function () {
var PAGES = { editor: "editor.html", initial: "player.html", teacher: "teacher.html",
stage: "stage.html", micro: "micro.html", showcase: "showcase.html" };
var me = document.currentScript;
var ORIGIN = me ? me.src.replace(/\/embed\.js(\?.*)?$/, "") : location.origin;
function build(el) {
var v = (el.getAttribute("data-varasys-metronome") || "micro").toLowerCase();
var page = PAGES[v] || "micro.html";
var patch = el.getAttribute("data-patch");
var sl = el.getAttribute("data-setlist");
var hash = patch ? "#p=" + encodeURIComponent(patch)
: sl ? "#sl=" + encodeURIComponent(sl) : "";
var f = document.createElement("iframe");
f.src = ORIGIN + "/" + page + "?embed=1" + hash;
f.title = "VARASYS PolyMeter — " + v;
f.loading = "lazy";
f.setAttribute("allow", "autoplay");
f.setAttribute("data-vmeter", v);
f.style.cssText = "border:0;display:block;max-width:100%;width:" +
(el.getAttribute("data-width") || "100%") + ";height:" +
(el.getAttribute("data-height") || "300") + "px";
(el.replaceWith ? el.replaceWith(f) : el.parentNode.replaceChild(f, el));
}
function init() {
var els = document.querySelectorAll("[data-varasys-metronome]");
for (var i = 0; i < els.length; i++) build(els[i]);
}
// auto-resize: the widget posts { type:'varasys-h', h } on load/resize
window.addEventListener("message", function (e) {
if (!e.data || e.data.type !== "varasys-h" || typeof e.data.h !== "number") return;
var f = document.querySelectorAll("iframe[data-vmeter]");
for (var i = 0; i < f.length; i++) if (f[i].contentWindow === e.source) f[i].style.height = e.data.h + "px";
});
if (document.readyState !== "loading") init();
else document.addEventListener("DOMContentLoaded", init);
})();