Embed version dropdown; Display weight static/behind lights; Practice beat flash; move Philosophy up
- Embed page: a form-factor dropdown that rewrites every snippet (drop-in + plain iframe), the live demo, and the name for the chosen version; variant table completed to all six. - Display (Showcase): the tempo weight no longer flashes and is drawn BEHIND the pendulum lights so it never hides a beat flash. - Practice (Micro): a beat/sub-beat flash — the whole 14-seg display washes amber on each step (subtle on sub-beats, brighter on the beat, full on the "1"), latency-compensated like the other devices. - Landing: Philosophy section moved above "Pick a form factor". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
36cff77219
commit
1faf9cad41
4 changed files with 106 additions and 41 deletions
64
embed.html
64
embed.html
|
|
@ -33,6 +33,10 @@
|
||||||
th{ color:var(--muted); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }
|
th{ color:var(--muted); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }
|
||||||
td.k{ white-space:nowrap; color:var(--cyan); font-family:"Courier New",monospace; }
|
td.k{ white-space:nowrap; color:var(--cyan); font-family:"Courier New",monospace; }
|
||||||
.demo{ background:var(--panel-bg); border:1px solid var(--panel-bd); border-radius:14px; padding:14px; margin-top:8px; }
|
.demo{ background:var(--panel-bg); border:1px solid var(--panel-bd); border-radius:14px; padding:14px; margin-top:8px; }
|
||||||
|
.pick{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:14px; }
|
||||||
|
.pick label{ font-size:13px; color:var(--txt); }
|
||||||
|
.pick select{ background:var(--field-bg); color:var(--txt); border:1px solid var(--panel-bd); border-radius:8px; padding:7px 10px; font-size:13px; }
|
||||||
|
.ff-name{ color:var(--cyan); font-weight:600; font-size:13px; }
|
||||||
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
.site-foot{ max-width:760px; margin:40px auto 0; font-size:12px; color:var(--muted); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -46,27 +50,35 @@
|
||||||
a placeholder + one script tag — no build step, no dependencies. It loads in an iframe and is preloaded
|
a placeholder + one script tag — no build step, no dependencies. It loads in an iframe and is preloaded
|
||||||
with whatever <b>program / settings string</b> you give it.</p>
|
with whatever <b>program / settings string</b> you give it.</p>
|
||||||
|
|
||||||
|
<p class="pick"><label for="ffSel">Show snippets for:</label>
|
||||||
|
<select id="ffSel">
|
||||||
|
<option value="editor">PM_E‑1 Editor</option>
|
||||||
|
<option value="teacher">PM_T‑1 Teacher</option>
|
||||||
|
<option value="stage">PM_S‑1 Stage</option>
|
||||||
|
<option value="micro" selected>PM_P‑1 Practice</option>
|
||||||
|
<option value="showcase">PM_D‑1 Display</option>
|
||||||
|
<option value="initial">PM_C‑1 Concept</option>
|
||||||
|
</select>
|
||||||
|
<span class="ff-name"></span></p>
|
||||||
|
|
||||||
<h2>Drop-in (recommended)</h2>
|
<h2>Drop-in (recommended)</h2>
|
||||||
<pre><div data-varasys-metronome="micro"
|
<pre id="snipDrop"></pre>
|
||||||
data-patch="v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2"></div>
|
<p>The script replaces the <code><div></code> with an auto-sizing iframe. Here's the <span class="ff-name"></span>, live on this page:</p>
|
||||||
<script src="https://metronome.varasys.io/embed.js"></script></pre>
|
<div class="demo"><iframe id="demoFrame" title="live embed preview" allow="autoplay" style="border:0;display:block;width:100%;height:300px"></iframe></div>
|
||||||
<p>The script replaces the <code><div></code> with an auto-sizing iframe. Here it is, live on this page:</p>
|
|
||||||
<div class="demo">
|
|
||||||
<div data-varasys-metronome="micro" data-patch="v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Or a plain iframe</h2>
|
<h2>Or a plain iframe</h2>
|
||||||
<pre><iframe src="https://metronome.varasys.io/micro.html?embed=1#p=v1;t120;kick:4"
|
<pre id="snipIframe"></pre>
|
||||||
width="360" height="300" style="border:0"></iframe></pre>
|
|
||||||
|
|
||||||
<h2>Form factors — <code>data-varasys-metronome</code></h2>
|
<h2>Form factors — <code>data-varasys-metronome</code></h2>
|
||||||
<table>
|
<table>
|
||||||
<thead><tr><th>value</th><th>widget</th></tr></thead>
|
<thead><tr><th>value</th><th>widget</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td class="k">editor</td><td>PM_E‑1 PolyMeter Editor (full app)</td></tr>
|
<tr><td class="k">editor</td><td>PM_E‑1 PolyMeter Editor (full web app)</td></tr>
|
||||||
<tr><td class="k">initial</td><td>PM_C‑1 Concept (idealized device)</td></tr>
|
|
||||||
<tr><td class="k">teacher</td><td>PM_T‑1 Teacher (studio / lesson console)</td></tr>
|
<tr><td class="k">teacher</td><td>PM_T‑1 Teacher (studio / lesson console)</td></tr>
|
||||||
|
<tr><td class="k">stage</td><td>PM_S‑1 Stage (foot‑pedal stompbox)</td></tr>
|
||||||
<tr><td class="k">micro</td><td>PM_P‑1 Practice (inline practice bar)</td></tr>
|
<tr><td class="k">micro</td><td>PM_P‑1 Practice (inline practice bar)</td></tr>
|
||||||
|
<tr><td class="k">showcase</td><td>PM_D‑1 Display (RGB pendulum showpiece)</td></tr>
|
||||||
|
<tr><td class="k">initial</td><td>PM_C‑1 Concept (idealized render)</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
@ -86,9 +98,37 @@
|
||||||
<script>
|
<script>
|
||||||
const APP_VERSION = "v0.0.1-dev";
|
const APP_VERSION = "v0.0.1-dev";
|
||||||
const $ = (id)=>document.getElementById(id);
|
const $ = (id)=>document.getElementById(id);
|
||||||
|
|
||||||
|
/* Form-factor picker: updates every snippet + the live demo for the chosen version. */
|
||||||
|
const ORIGIN = "https://metronome.varasys.io";
|
||||||
|
const DEMO_PATCH = "v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2";
|
||||||
|
const FF = [
|
||||||
|
{ k:"editor", name:"PM_E‑1 Editor", file:"editor.html", h:560 },
|
||||||
|
{ k:"teacher", name:"PM_T‑1 Teacher", file:"teacher.html", h:440 },
|
||||||
|
{ k:"stage", name:"PM_S‑1 Stage", file:"stage.html", h:430 },
|
||||||
|
{ k:"micro", name:"PM_P‑1 Practice", file:"micro.html", h:240 },
|
||||||
|
{ k:"showcase", name:"PM_D‑1 Display", file:"showcase.html",h:540 },
|
||||||
|
{ k:"initial", name:"PM_C‑1 Concept", file:"player.html", h:440 },
|
||||||
|
];
|
||||||
|
function updateFF(k){
|
||||||
|
const v = FF.find(x => x.k === k) || FF[0];
|
||||||
|
$("snipDrop").textContent =
|
||||||
|
'<div data-varasys-metronome="' + v.k + '"\n data-patch="' + DEMO_PATCH + '"></div>\n' +
|
||||||
|
'<script src="' + ORIGIN + '/embed.js"><\/script>';
|
||||||
|
$("snipIframe").textContent =
|
||||||
|
'<iframe src="' + ORIGIN + '/' + v.file + '?embed=1#p=' + DEMO_PATCH + '"\n' +
|
||||||
|
' width="360" height="' + v.h + '" style="border:0"><\/iframe>';
|
||||||
|
const f = $("demoFrame"); f.style.height = v.h + "px"; f.src = "/" + v.file + "?embed=1#p=" + encodeURIComponent(DEMO_PATCH);
|
||||||
|
document.querySelectorAll(".ff-name").forEach(el => el.textContent = v.name);
|
||||||
|
}
|
||||||
|
$("ffSel").addEventListener("change", (e) => updateFF(e.target.value));
|
||||||
|
addEventListener("message", (e) => {
|
||||||
|
if (e.data && e.data.type === "varasys-h" && typeof e.data.h === "number" && e.source === $("demoFrame").contentWindow)
|
||||||
|
$("demoFrame").style.height = e.data.h + "px";
|
||||||
|
});
|
||||||
|
updateFF($("ffSel").value || "micro");
|
||||||
/*@BUILD:include:src/chrome.js@*/
|
/*@BUILD:include:src/chrome.js@*/
|
||||||
</script>
|
</script>
|
||||||
<script src="/embed.js"></script>
|
|
||||||
/*@BUILD:include:src/footer.html@*/
|
/*@BUILD:include:src/footer.html@*/
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
32
index.html
32
index.html
|
|
@ -89,6 +89,22 @@
|
||||||
below — or pick any form factor to load and play the same groove on it.</p>
|
below — or pick any form factor to load and play the same groove on it.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="philosophy">
|
||||||
|
<div class="section-label">Philosophy</div>
|
||||||
|
<div class="phil-grid">
|
||||||
|
<div class="phil">
|
||||||
|
<h3>🛠️ Program on the web, play on any device</h3>
|
||||||
|
<p>The website is the workbench: design in the <a href="/editor.html">editor</a>, and the same
|
||||||
|
<b>program string</b> loads into whichever form factor fits the moment. One engine, one language.</p>
|
||||||
|
</div>
|
||||||
|
<div class="phil">
|
||||||
|
<h3>🔌 USB‑C power everywhere — no batteries</h3>
|
||||||
|
<p>Every device runs over a single <b>USB‑C</b> port (the larger ones add a pass‑through to daisy‑chain).
|
||||||
|
No internal battery to wear out; bring a power bank. One connector keeps it all <b>future‑proof</b>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="section-label">Pick a form factor — it loads live below</div>
|
<div class="section-label">Pick a form factor — it loads live below</div>
|
||||||
<div class="panes" id="panes"></div>
|
<div class="panes" id="panes"></div>
|
||||||
|
|
||||||
|
|
@ -107,22 +123,6 @@
|
||||||
<div class="prog-hint">The current program, decoded (not base64). Paste a patch <i>or</i> a base64 set‑list code; it's checked, then loaded.
|
<div class="prog-hint">The current program, decoded (not base64). Paste a patch <i>or</i> a base64 set‑list code; it's checked, then loaded.
|
||||||
Conventions: GM names or numbers (<code>kick</code> / <code>36</code>), <code>=X.x-</code> steps, <code>/2</code> subdivision, <code>(3,8)</code> euclidean, <code>@-3</code> dB, <code>~</code> polymeter.</div>
|
Conventions: GM names or numbers (<code>kick</code> / <code>36</code>), <code>=X.x-</code> steps, <code>/2</code> subdivision, <code>(3,8)</code> euclidean, <code>@-3</code> dB, <code>~</code> polymeter.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="philosophy">
|
|
||||||
<div class="section-label">Philosophy</div>
|
|
||||||
<div class="phil-grid">
|
|
||||||
<div class="phil">
|
|
||||||
<h3>🛠️ Program on the web, play on any device</h3>
|
|
||||||
<p>The website is the workbench: design in the <a href="/editor.html">editor</a>, and the same
|
|
||||||
<b>program string</b> loads into whichever form factor fits the moment. One engine, one language.</p>
|
|
||||||
</div>
|
|
||||||
<div class="phil">
|
|
||||||
<h3>🔌 USB‑C power everywhere — no batteries</h3>
|
|
||||||
<p>Every device runs over a single <b>USB‑C</b> port (the larger ones add a pass‑through to daisy‑chain).
|
|
||||||
No internal battery to wear out; bring a power bank. One connector keeps it all <b>future‑proof</b>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
/*@BUILD:include:src/footer.html@*/
|
/*@BUILD:include:src/footer.html@*/
|
||||||
|
|
|
||||||
30
micro.html
30
micro.html
|
|
@ -201,7 +201,7 @@ function startAudio(){
|
||||||
ensureAudio(); audioCtx.resume(); state.running=true;
|
ensureAudio(); audioCtx.resume(); state.running=true;
|
||||||
const t0=audioCtx.currentTime+0.08;
|
const t0=audioCtx.currentTime+0.08;
|
||||||
for(const m of meters){ m.tick=0; m.nextTime=t0; m.vq=[]; m.vqPtr=0; }
|
for(const m of meters){ m.tick=0; m.nextTime=t0; m.vq=[]; m.vqPtr=0; }
|
||||||
muteWindows=[]; schedulerTimer=setInterval(scheduler,LOOKAHEAD_MS); scheduler(); render();
|
muteWindows=[]; beatFlash=0; schedulerTimer=setInterval(scheduler,LOOKAHEAD_MS); scheduler(); render();
|
||||||
}
|
}
|
||||||
function stopAudio(){ state.running=false; clearInterval(schedulerTimer); schedulerTimer=null; render(); }
|
function stopAudio(){ state.running=false; clearInterval(schedulerTimer); schedulerTimer=null; render(); }
|
||||||
function toggle(){ state.running ? stopAudio() : startAudio(); }
|
function toggle(){ state.running ? stopAudio() : startAudio(); }
|
||||||
|
|
@ -262,11 +262,36 @@ function drawChar(dx,dy,w,h,ch){
|
||||||
diag(11, dx+t+1, dy+h-t-1, cx-t*0.6, midY+t*0.6); // K bottom-left
|
diag(11, dx+t+1, dy+h-t-1, cx-t*0.6, midY+t*0.6); // K bottom-left
|
||||||
diag(13, dx+w-t-1, dy+h-t-1, cx+t*0.6, midY+t*0.6); // M bottom-right
|
diag(13, dx+w-t-1, dy+h-t-1, cx+t*0.6, midY+t*0.6); // M bottom-right
|
||||||
}
|
}
|
||||||
function drawLED(){
|
function drawLED(flash, level){
|
||||||
|
flash = flash || 0; level = level || 0;
|
||||||
lc.fillStyle=LED_BG; lc.fillRect(0,0,LW,LH);
|
lc.fillStyle=LED_BG; lc.fillRect(0,0,LW,LH);
|
||||||
|
// whole-display beat flash: a wash over the window — subtle on sub-beats, bright on the "1"
|
||||||
|
if(flash>0){ const a = flash * (level>=3 ? 0.55 : level>=2 ? 0.30 : 0.13);
|
||||||
|
lc.fillStyle = (level>=3 ? "rgba(255,184,74," : "rgba(255,138,30,") + a.toFixed(3) + ")"; lc.fillRect(0,0,LW,LH); }
|
||||||
const txt=ledText(), pad=10, gap=9, n=NCH, dw=(LW-2*pad-(n-1)*gap)/n, dh=LH-2*pad;
|
const txt=ledText(), pad=10, gap=9, n=NCH, dw=(LW-2*pad-(n-1)*gap)/n, dh=LH-2*pad;
|
||||||
for(let i=0;i<n;i++) drawChar(pad+i*(dw+gap), pad, dw, dh, txt[i]||" ");
|
for(let i=0;i<n;i++) drawChar(pad+i*(dw+gap), pad, dw, dh, txt[i]||" ");
|
||||||
}
|
}
|
||||||
|
// beat / sub-beat flash, driven off the master lane (latency-compensated, like the other devices)
|
||||||
|
let beatFlash=0, beatLevel=0;
|
||||||
|
function audioLatency(){ return audioCtx ? (audioCtx.outputLatency || audioCtx.baseLatency || 0) : 0; }
|
||||||
|
function frame(){
|
||||||
|
if(audioCtx && state.running){
|
||||||
|
const now = audioCtx.currentTime - audioLatency();
|
||||||
|
for(const m of meters){
|
||||||
|
while(m.vqPtr<m.vq.length && m.vq[m.vqPtr].time<=now){
|
||||||
|
const e=m.vq[m.vqPtr]; m.currentStep=e.step;
|
||||||
|
if(m===meters[0]){ const spb=m.stepsPerBeat;
|
||||||
|
beatLevel = (e.step===0) ? 3 : (e.step % spb === 0) ? 2 : 1; // downbeat ("1") · beat · sub-beat
|
||||||
|
if((m.beatsOn[e.step]|0)!==0 || e.step % spb === 0) beatFlash=1; }
|
||||||
|
m.vqPtr++;
|
||||||
|
}
|
||||||
|
if(m.vqPtr>512){ m.vq=m.vq.slice(m.vqPtr); m.vqPtr=0; }
|
||||||
|
}
|
||||||
|
beatFlash = Math.max(0, beatFlash - 0.10);
|
||||||
|
drawLED(beatFlash, beatLevel);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
function render(){
|
function render(){
|
||||||
drawLED();
|
drawLED();
|
||||||
$("indBpm").classList.toggle("on", displayMode==="bpm");
|
$("indBpm").classList.toggle("on", displayMode==="bpm");
|
||||||
|
|
@ -315,6 +340,7 @@ addEventListener("keydown",(e)=>{
|
||||||
{ const ht=tracksFromHash(); if(ht) tracks=ht; } // a #p=/#sl= link (or embed config) overrides the built-ins
|
{ const ht=tracksFromHash(); if(ht) tracks=ht; } // a #p=/#sl= link (or embed config) overrides the built-ins
|
||||||
loadTrack(0);
|
loadTrack(0);
|
||||||
render();
|
render();
|
||||||
|
requestAnimationFrame(frame);
|
||||||
window.currentProgramString = function(){ var t=tracks[trackIdx]||{}; return setupToPatch({bpm:state.bpm, volume:state.volume, lanes:t.lanes||[]}); };
|
window.currentProgramString = function(){ var t=tracks[trackIdx]||{}; return setupToPatch({bpm:state.bpm, volume:state.volume, lanes:t.lanes||[]}); };
|
||||||
window.loadProgramString = function(plain){ var s=patchToSetup(plain); tracks=[{name:"Program", ...s}]; trackIdx=0; loadTrack(0); };
|
window.loadProgramString = function(plain){ var s=patchToSetup(plain); tracks=[{name:"Program", ...s}]; trackIdx=0; loadTrack(0); };
|
||||||
/*@BUILD:include:src/progbox.js@*/
|
/*@BUILD:include:src/progbox.js@*/
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,16 @@ function drawPendulum(){
|
||||||
[40,60,80,100,120,160,200,240].forEach(function(b){ const y=-bpmToFrac(b)*ROD;
|
[40,60,80,100,120,160,200,240].forEach(function(b){ const y=-bpmToFrac(b)*ROD;
|
||||||
g.strokeStyle="rgba(180,190,205,.5)"; g.lineWidth=1; g.beginPath(); g.moveTo(4,y); g.lineTo(10,y); g.stroke();
|
g.strokeStyle="rgba(180,190,205,.5)"; g.lineWidth=1; g.beginPath(); g.moveTo(4,y); g.lineTo(10,y); g.stroke();
|
||||||
g.fillStyle="rgba(180,190,205,.6)"; g.fillText(String(b), 2, y+3); });
|
g.fillStyle="rgba(180,190,205,.6)"; g.fillText(String(b), 2, y+3); });
|
||||||
// combined-meter LEDs: each lane is a moving point of light along the bar (its current step position)
|
// fixed bob (drives the swing) near the bottom of the rod
|
||||||
|
g.fillStyle="#2a2f37"; roundRectP(-9,-58,18,30,4); g.fill();
|
||||||
|
g.fillStyle="rgba(255,255,255,.06)"; roundRectP(-9,-58,18,5,2); g.fill();
|
||||||
|
// sliding WEIGHT = tempo — STATIC (no flash) and drawn BEHIND the lights so it never hides a beat flash
|
||||||
|
const wy=-bpmToFrac(state.bpm)*ROD;
|
||||||
|
g.fillStyle="#3a4049"; roundRectP(-15,wy-11,30,22,4); g.fill();
|
||||||
|
g.fillStyle="rgba(150,160,176,.5)"; roundRectP(-13,wy-3.5,26,7,2); g.fill(); // index mark (static)
|
||||||
|
g.fillStyle="rgba(255,255,255,.10)"; roundRectP(-15,wy-11,30,5,2); g.fill(); // top sheen
|
||||||
|
// combined-meter LEDs: each lane is a moving point of light along the bar (its current step position).
|
||||||
|
// Drawn LAST so the flashes sit on top of the weight.
|
||||||
for(const m of meters){ if(m.currentStep<0 || !state.running) continue;
|
for(const m of meters){ if(m.currentStep<0 || !state.running) continue;
|
||||||
const steps=m.beatsPerBar*m.stepsPerBeat, fr=steps?((m.currentStep%steps)/steps):0;
|
const steps=m.beatsPerBar*m.stepsPerBeat, fr=steps?((m.currentStep%steps)/steps):0;
|
||||||
const y=-(0.16 + fr*(0.96-0.16))*ROD, lvl=m.beatsOn[m.currentStep]|0; if(lvl===0) continue;
|
const y=-(0.16 + fr*(0.96-0.16))*ROD, lvl=m.beatsOn[m.currentStep]|0; if(lvl===0) continue;
|
||||||
|
|
@ -248,16 +257,6 @@ function drawPendulum(){
|
||||||
g.shadowColor=rgb; g.shadowBlur=14; g.fillStyle=rgb;
|
g.shadowColor=rgb; g.shadowBlur=14; g.fillStyle=rgb;
|
||||||
g.beginPath(); g.arc(0,y, lvl>=2?6:4.5, 0,7); g.fill(); g.shadowBlur=0;
|
g.beginPath(); g.arc(0,y, lvl>=2?6:4.5, 0,7); g.fill(); g.shadowBlur=0;
|
||||||
}
|
}
|
||||||
// fixed bob (drives the swing) near the bottom of the rod
|
|
||||||
g.fillStyle="#2a2f37"; roundRectP(-9,-58,18,30,4); g.fill();
|
|
||||||
g.fillStyle="rgba(255,255,255,.06)"; roundRectP(-9,-58,18,5,2); g.fill();
|
|
||||||
// sliding WEIGHT = tempo; glows on the beat
|
|
||||||
const wy=-bpmToFrac(state.bpm)*ROD, lit=Math.max(0,flash);
|
|
||||||
const wc = flashAccent ? "rgb(255,155,46)" : "rgb(51,208,255)";
|
|
||||||
g.shadowColor=wc; g.shadowBlur=8+22*lit;
|
|
||||||
g.fillStyle="#3a4049"; roundRectP(-15,wy-11,30,22,4); g.fill(); g.shadowBlur=0;
|
|
||||||
g.fillStyle=wc; g.globalAlpha=.30+0.7*lit; roundRectP(-13,wy-3.5,26,7,2); g.fill(); g.globalAlpha=1;
|
|
||||||
g.fillStyle="rgba(255,255,255,.10)"; roundRectP(-15,wy-11,30,5,2); g.fill();
|
|
||||||
// pivot hub
|
// pivot hub
|
||||||
g.restore();
|
g.restore();
|
||||||
g.beginPath(); g.arc(PIVX,PIVY,6,0,7); g.fillStyle="#2a2f37"; g.fill();
|
g.beginPath(); g.arc(PIVX,PIVY,6,0,7); g.fillStyle="#2a2f37"; g.fill();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue