seed lists + mobile icons: rich Styles/Practice, drop Song/Notation, subtle music theme

setlists.js (shared by all pages):
- Removed the "Song (continuous)" and "Notation showcase" seed lists.
- "Styles" is now a rich, genre-true collection (16): rock, pop 16ths, funk,
  disco, Motown, blues shuffle, jazz swing, bossa, samba, reggae one-drop,
  afrobeat, hip-hop, metal, 6/8 ballad, 7/8, 5/4 — full grooves to jam over.
- "Practice" is 15 drummer drills to learn those styles: hat subdivisions,
  ghost-note backbeats, 16th hand control, shuffle/jazz ride, bossa & 3-over-4
  independence, dynamics, double bass, hemiola/5-over-4, tempo & gap trainers.
- Dropped the cartoon emoji from the titles. All patches validated: every lane
  parses and pattern lengths match their meters.

Mobile icons — less cartoonish, subtly musical:
- Volume rail now reads p … f (piano/forte dynamics) instead of speaker emoji.
- Save 💾 -> ↧; library +/✕ instead of /🗑.
- Practice-sessions empty state uses a treble clef instead of 🎼.

Engine untouched; conformance passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-06-07 11:14:06 -05:00
parent ca2a695f4f
commit 4b53f917f4
3 changed files with 48 additions and 55 deletions

View file

@ -174,7 +174,7 @@ function renderList(){
const list=$("list"); list.innerHTML=""; const list=$("list"); list.innerHTML="";
if(!sessions.length){ if(!sessions.length){
$("listLabel").style.display="none"; $("listLabel").style.display="none";
list.innerHTML='<div class="empty"><div class="big">🎼</div><p>No practice sessions yet.<br>On the metronome, press <b>Practice</b> to start a session, then <b>Stop</b> when you\'re done — it\'ll be saved here.</p></div>'; list.innerHTML='<div class="empty"><div class="big">&#119070;</div><p>No practice sessions yet.<br>On the metronome, press <b>Practice</b> to start a session, then <b>Stop</b> when you\'re done — it\'ll be saved here.</p></div>';
return; return;
} }
$("listLabel").style.display=""; $("listLabel").style.display="";

View file

@ -61,8 +61,9 @@
.sel > span{ font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); padding-left:3px; } .sel > span{ font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); padding-left:3px; }
.sel select{ width:100%; background:var(--field-bg); color:var(--txt); border:1px solid var(--field-bd); border-radius:10px; padding:10px 8px; font-size:15px; } .sel select{ width:100%; background:var(--field-bg); color:var(--txt); border:1px solid var(--field-bd); border-radius:10px; padding:10px 8px; font-size:15px; }
.trow{ display:flex; align-items:center; gap:10px; } .trow{ display:flex; align-items:center; gap:10px; }
.vol{ flex:1 1 auto; display:flex; align-items:center; gap:9px; color:var(--muted); font-size:15px; min-width:0; } .vol{ flex:1 1 auto; display:flex; align-items:center; gap:10px; color:var(--muted); min-width:0; }
.vol input{ flex:1 1 auto; min-width:0; accent-color:var(--cyan); } .vol input{ flex:1 1 auto; min-width:0; accent-color:var(--cyan); }
.dyn{ flex:0 0 auto; font-family:Georgia,"Times New Roman",serif; font-style:italic; font-weight:700; font-size:17px; color:var(--muted); line-height:1; }
.icon{ flex:0 0 auto; width:42px; height:42px; border-radius:50%; display:flex; align-items:center; justify-content:center; .icon{ flex:0 0 auto; width:42px; height:42px; border-radius:50%; display:flex; align-items:center; justify-content:center;
font-size:18px; line-height:1; cursor:pointer; color:var(--txt); background:rgba(127,139,154,.14); border:1px solid var(--panel-bd); } font-size:18px; line-height:1; cursor:pointer; color:var(--txt); background:rgba(127,139,154,.14); border:1px solid var(--panel-bd); }
.icon:active{ background:rgba(127,139,154,.30); } .icon:active{ background:rgba(127,139,154,.30); }
@ -210,8 +211,8 @@
<label class="sel"><span>Track</span><select id="trkSel"></select></label> <label class="sel"><span>Track</span><select id="trkSel"></select></label>
</div> </div>
<div class="trow"> <div class="trow">
<div class="vol">🔈<input id="vol" type="range" min="0" max="100" value="85" />🔊</div> <div class="vol"><span class="dyn" aria-hidden="true">p</span><input id="vol" type="range" min="0" max="100" value="85" aria-label="Volume" /><span class="dyn" aria-hidden="true">f</span></div>
<div class="icon" id="saveBtn" title="Save &amp; library" aria-label="Save and library">💾</div> <div class="icon" id="saveBtn" title="Save &amp; library" aria-label="Save and library"></div>
<div class="icon" id="helpBtn" title="Help" aria-label="Help">?</div> <div class="icon" id="helpBtn" title="Help" aria-label="Help">?</div>
<div class="icon" id="themeBtn" title="Theme" aria-label="Theme"></div> <div class="icon" id="themeBtn" title="Theme" aria-label="Theme"></div>
<div class="icon" id="fsBtn" title="Full screen" aria-label="Full screen"></div> <div class="icon" id="fsBtn" title="Full screen" aria-label="Full screen"></div>
@ -570,7 +571,7 @@ function newList(){ const n=prompt("New set list name:","My set list"); if(n==nu
function buildSaveTo(){ savedLists=userSetlists(); const sel=$("saveTo"); sel.innerHTML=""; function buildSaveTo(){ savedLists=userSetlists(); const sel=$("saveTo"); sel.innerHTML="";
savedLists.forEach((sl,i)=>sel.appendChild(opt("s"+i,(sl.title||("Set list "+(i+1)))+" ("+(sl.items?sl.items.length:0)+")"))); savedLists.forEach((sl,i)=>sel.appendChild(opt("s"+i,(sl.title||("Set list "+(i+1)))+" ("+(sl.items?sl.items.length:0)+")")));
sel.appendChild(opt("__new"," New set list…")); sel.appendChild(opt("__new","+ New set list…"));
sel.value = slKey[0]==="s" ? slKey : (savedLists.length?"s0":"__new"); sel.value = slKey[0]==="s" ? slKey : (savedLists.length?"s0":"__new");
$("saveNewName").style.display = sel.value==="__new" ? "block":"none"; } $("saveNewName").style.display = sel.value==="__new" ? "block":"none"; }
$("saveTo").onchange=()=>{ $("saveNewName").style.display = $("saveTo").value==="__new" ? "block":"none"; }; $("saveTo").onchange=()=>{ $("saveNewName").style.display = $("saveTo").value==="__new" ? "block":"none"; };
@ -581,15 +582,15 @@ function renderLibrary(){ savedLists=userSetlists(); const box=$("libBody"); box
savedLists.forEach((sl,i)=>{ const row=el("div","librow"+(slKey==="s"+i?" active":"")); savedLists.forEach((sl,i)=>{ const row=el("div","librow"+(slKey==="s"+i?" active":""));
const nm=el("button","libname",(sl.title||"set list")+" ("+(sl.items?sl.items.length:0)+")"); nm.onclick=()=>selectUserList(i); row.appendChild(nm); const nm=el("button","libname",(sl.title||"set list")+" ("+(sl.items?sl.items.length:0)+")"); nm.onclick=()=>selectUserList(i); row.appendChild(nm);
row.appendChild(ibtn("↑",()=>moveList(i,-1),i===0)); row.appendChild(ibtn("↓",()=>moveList(i,1),i===savedLists.length-1)); row.appendChild(ibtn("↑",()=>moveList(i,-1),i===0)); row.appendChild(ibtn("↓",()=>moveList(i,1),i===savedLists.length-1));
row.appendChild(ibtn("✎",()=>renameList(i))); row.appendChild(ibtn("🗑",()=>deleteList(i))); box.appendChild(row); }); row.appendChild(ibtn("✎",()=>renameList(i))); row.appendChild(ibtn("",()=>deleteList(i))); box.appendChild(row); });
const addL=el("button","addlane"," New set list"); addL.onclick=newList; box.appendChild(addL); const addL=el("button","addlane","+ New set list"); addL.onclick=newList; box.appendChild(addL);
if(slKey[0]==="s"){ const i=+slKey.slice(1), sl=savedLists[i]; if(sl){ if(slKey[0]==="s"){ const i=+slKey.slice(1), sl=savedLists[i]; if(sl){
box.appendChild(el("div","liblbl","Tracks in “"+(sl.title||"set list")+"”")); box.appendChild(el("div","liblbl","Tracks in “"+(sl.title||"set list")+"”"));
sl.items.forEach((it,j)=>{ const row=el("div","librow"+(idx===j?" active":"")); sl.items.forEach((it,j)=>{ const row=el("div","librow"+(idx===j?" active":""));
const nm=el("button","libname",(j+1)+". "+(it.name||"track")); nm.onclick=()=>{ gotoItem(j,state.running); renderLibrary(); }; row.appendChild(nm); const nm=el("button","libname",(j+1)+". "+(it.name||"track")); nm.onclick=()=>{ gotoItem(j,state.running); renderLibrary(); }; row.appendChild(nm);
row.appendChild(ibtn("↑",()=>moveTrack(i,j,-1),j===0)); row.appendChild(ibtn("↓",()=>moveTrack(i,j,1),j===sl.items.length-1)); row.appendChild(ibtn("↑",()=>moveTrack(i,j,-1),j===0)); row.appendChild(ibtn("↓",()=>moveTrack(i,j,1),j===sl.items.length-1));
row.appendChild(ibtn("✎",()=>renameTrack(i,j))); row.appendChild(ibtn("🗑",()=>deleteTrack(i,j))); box.appendChild(row); }); row.appendChild(ibtn("✎",()=>renameTrack(i,j))); row.appendChild(ibtn("",()=>deleteTrack(i,j))); box.appendChild(row); });
const addT=el("button","addlane"," Add current track here"); addT.onclick=()=>{ $("saveTo").value="s"+i; doSaveAsNew(); }; box.appendChild(addT); const addT=el("button","addlane","+ Add current track here"); addT.onclick=()=>{ $("saveTo").value="s"+i; doSaveAsNew(); }; box.appendChild(addT);
}} else { box.appendChild(el("div","libhint","This set list is built-in (read-only). “Save as new track” copies your edits into one of your own set lists.")); } }} else { box.appendChild(el("div","libhint","This set list is built-in (read-only). “Save as new track” copies your edits into one of your own set lists.")); }
} }
function openSaveSheet(){ function openSaveSheet(){

View file

@ -1,50 +1,42 @@
// Demo set list (each item authored in the share language — also exercises the parser). // Seed set lists — baked into every page; each item is authored in the share language
// (so this also exercises the parser). Two curated lists:
// Styles — full, genre-true grooves; good as backing tracks to jam/solo over.
// Practice — drills for learning to PLAY those styles (isolations, independence,
// dynamics, polyrhythms, tempo/gap tools).
const SEED_SETLISTS = [ const SEED_SETLISTS = [
{ title: "🥁 Styles", description: "Grooves & feels — load one, press Space, and click pads to shape the accents.", items: [ { title: "Styles", description: "Full genre grooves — load one, press play, and jam over it. Tap pads to reshape the feel.", items: [
["Four-on-the-floor", "t120;kick:4;snare:4=.x.x;hatClosed:4/2"], ["Rock", "t116;kick:4/2=X...Xx..;snare:4/2=..X...X.;hatClosed:4/2=Xxxxxxxx"],
["Swing ride", "t150;ride:4/2s;kick:4=X..x;snare:4=.x.x"], ["Pop (16ths)", "t104;kick:4/4=X.....x...X.....;snare:4/4=....X.......X...;hatClosed:4/4=xxxxxxxxxxxxxxxx"],
// Purdie half-time shuffle: triplet grid, backbeat on 3, snare ghosts (normal) around it ["Funk", "t100;kick:4/4=X..x..X...x.X...;snare:4/4=....X..g.g..X.g.;hatClosed:4/4=xxxxxxxxxxxxxxxx"],
["Purdie half-time shuffle", "t92;kick:4/3=X....x...x..;snare:4/3=..gg.gX.gg.g;hatClosed:4/3=X.xX.xX.xX.x"], ["Disco", "t120;kick:4;clap:4=.X.X;hatClosed:4/2=X.X.X.X.;hatOpen:4/2=.x.x.x.x"],
// Samba in 2/4 (16ths): surdo strong on beat 2, steady ganzá, tamborim teleco-teco ["Motown backbeat", "t132;kick:4/2=X..x.X..;snare:4/2=..X...X.;tambourine:4/2=xxxxxxxx"],
["Samba (2/4)", "t104;tomLow:2/4=x...X...;hatClosed:2/4;woodblock:2/4=X.xx.xX."], ["Blues shuffle (12/8)", "t84;ride:4/3=X.xX.xX.xX.x;snare:4/3=...X.....X..;kick:4/3=X.....X....."],
// Nañigo / 6/8 bembé bell over a 12/8 grid, low drum on the two main pulses ["Jazz swing", "t150;ride:4/3=X.xX.xX.xX.x;hatClosed:4=.x.x;kick:4=x.x.@-9"],
["Nañigo (6/8 bembé)", "t130;cowbell:4/3=X.xx.x.xx.x.;kick:4/3=X.....X.....;hatClosed:4/3=..x..x..x..x"], ["Bossa nova", "t128;rim:4/4=..x.x...x..x..x.;kick:4/2=X..X.X..;hatClosed:4/2=xxxxxxxx"],
["6/8 groove", "t100;kick:3+3=x..x..;snare:3+3=...x..;hatClosed:3+3/2"], ["Samba (2/4)", "t102;tomLow:2/4=x...X...;hatClosed:2/4=xxxxxxxx;woodblock:2/4=X.xx.xX."],
["7/8 (2+2+3)", "t130;kick:2+2+3=x..x..x;hatClosed:2+2+3/2"], ["Reggae one-drop", "t74;kick:4=..X.;rim:4=..X.;hatClosed:4/2=.x.x.x.x"],
["5/4 (3+2)", "t112;kick:3+2=x..x.;snare:3+2=..x..;hatClosed:3+2/2"], ["Afrobeat", "t108;cowbell:4/4=x.xx.xx.x.xx.xx.;kick:4/4=X..x..X..x..X...;snare:4/4=....g..X..g..X.g;hatClosed:4/4=xxxxxxxxxxxxxxxx"],
["Hip-hop (boom bap)", "t88;kick808:4/4=X.....x....X....;snare808:4/4=....X.......X...;hat808:4/4=x.x.x.x.x.x.x.x."],
["Metal driving", "t168;kick:4/2=XxXxXxXx;snare:4/2=..X...X.;hatClosed:4/2=Xxxxxxxx;crash:4=X..."],
["6/8 ballad", "t66;kick:3+3=X..X..;snare:3+3=...X..;hatClosed:3+3/2=xxxxxxxxxxxx"],
["7/8 (2+2+3)", "t130;kick:2+2+3=X..X..X;snare:2+2+3=..X..X.;hatClosed:2+2+3/2"],
["5/4 (3+2)", "t112;kick:3+2=X..X.;snare:3+2=..X..;hatClosed:3+2/2"],
] }, ] },
{ title: "🎯 Practice", description: "Polyrhythms, independence and tempo / gap tools.", items: [ { title: "Practice", description: "Drills to learn the styles - isolations, independence, dynamics, polyrhythms, and tempo / gap tools.", items: [
["5 over 4 polyrhythm", "t100;kick:4;claves:5~"], ["Rock beat - quarter hats", "t80;kick:4=X.X.;snare:4=.X.X;hatClosed:4=xxxx"],
["Rock beat - 8th hats", "t96;kick:4/2=X...Xx..;snare:4/2=..X...X.;hatClosed:4/2=xxxxxxxx"],
["Backbeat + ghost notes", "t90;kick:4/4=X.....x...X.....;snare:4/4=..g.X.g.g.g.X.g.;hatClosed:4/2=xxxxxxxx"],
["16th-note hand control", "t84;kick:4=X..X;snare:4/4=x.xxX.xxx.xxX.xx"],
["Shuffle feel (triplets)", "t92;hatClosed:4/3=X.xX.xX.xX.x;snare:4/3=...X.....X..;kick:4/3=X.....X....."],
["Jazz ride - spang-a-lang", "t140;ride:4/3=X.xX.xX.xX.x;hatClosed:4=.x.x;kick:4=x.x.@-9"],
["Bossa independence", "t120;rim:4/4=..x.x...x..x..x.;kick:4/2=X..X.X..;hatClosed:4=.x.x"],
["Linear funk", "t96;kick:4/4=X..x..X.....x...;snare:4/4=....X......X..g.;hatClosed:4/4=x.x.x.xxx.x.x.x."],
["Accent / ghost dynamics", "t88;snare:4/4=X.g.x.g.X.g.x.g.;hatClosed:4/2=xxxxxxxx;kick:4=X..X"],
["Double-bass workout", "t120;kick:4/4=xxxxxxxxxxxxxxxx;snare:4=.X.X;crash:4=X..."],
["Independence: 3 over 4", "t96;kick:4;hatClosed:4/2=xxxxxxxx;cowbell:3~"],
["3 over 2 hemiola", "t96;woodblock:2;cowbell:3~"], ["3 over 2 hemiola", "t96;woodblock:2;cowbell:3~"],
["2 & 4 & 3 over one bar", "t100;kick:3;cowbell:2~;claves:4~"], ["5 over 4 polyrhythm", "t100;kick:4;claves:5~"],
["Triplet hats", "t100;kick:4;snare:4=.x.x;hatClosed:4/3"], ["Tempo builder 80 up", "t80;kick:4=X.X.;snare:4=.X.X;hatClosed:4/2=xxxxxxxx;rmp80/4/4"],
["Accents — cycle the pads", "t92;kick:4=X..X;snare:4=.X.X;hatClosed:4/2"], ["Gap trainer (play 2 / rest 2)", "t100;kick:4/2=X...Xx..;snare:4/2=..X...X.;hatClosed:4/2=xxxxxxxx;tr2/2"],
["Tempo builder 80↑", "t80;woodblock:4;rmp80/4/4"],
["Gap trainer (play 2 / rest 2)", "t100;kick:4;hatClosed:4/2;tr2/2"],
] },
// A continuous ~4:00 song: each item has a bar length (b<n>) so it auto-advances (with
// Continue on) through tempo ramps and shifting styles. Durations ≈ bars × beats × 60/bpm.
{ title: "🎵 Song — continuous (~4:00)", description: "A full song: turn on Continue, press ▶ on “Intro”, and it plays straight through (~4 min) — segments auto-advance on their bar counts, through tempo ramps and shifting styles.", items: [
["Intro — hats & kick", "t88;b8;kick:4=X.x.;hatClosed:4/2=gggggggg"],
["Groove in — backbeat", "t88;b16;kick:4=X.x.;snare:4=.X.X;hatClosed:4/2"],
["Half-time shuffle", "t92;b12;kick:4/3=X....x...x..;snare:4/3=..gg.gX.gg.g;hatClosed:4/3=X.xX.xX.xX.x"],
["Build — ramp 92→120", "t92;b16;rmp92/4/2;kick:4;snare:4=.X.X;hatClosed:4/2"],
["Four-on-the-floor (909)", "t124;b18;kick909:4;clap909:4=.X.X;hat909:4/2=.X.X.X.X"],
["Samba break (2/4)", "t116;b24;tomLow:2/4=x...X...;hatClosed:2/4;woodblock:2/4=X.xx.xX."],
["Peak — 16ths", "t132;b16;kick:4=X..x;snare:4=.X.X;hatClosed:4/4"],
["Outro — ramp down", "t132;b8;rmp132/-7/1;kick:4=X..x;hatClosed:4/2=gggggggg"],
] },
// Showcase for the PM_E-2 engraved notation view — one groove per notation feature; also the
// visual test battery. Exercises the flam/drag/roll grammar (f/F d/D z/Z), ghosts, odd meters,
// clave (Latin), a West-African bell (read it in TUBS), and a polyrhythm.
{ title: "📜 Notation showcase", description: "Open pm_e-2.html (the Notation editor) and step through these — each shows a different notation feature: dynamics, ghosts, flams/drags/rolls, odd-meter beaming, clave, a 12/8 bell, and a polyrhythm.", items: [
["Rock 4/4 — the basics", "t120;kick:4=x.x.;snare:4=.x.x;hatClosed:4/2=xxxxxxxx"],
["Funk ghosts", "t96;kick:4/2=X..x.x..;snare:4/2=..g.X.g.;hatClosed:4/4=xxxxxxxxxxxxxxxx"],
["Flams & rolls", "t90;kick:4=x.x.;snare:4=F.fz;hatClosed:4/2=xxxxxxxx"],
["Drags (ruffs)", "t100;kick:4=x..x;snare:4=d.D.;hatClosed:4/2=xxxxxxxx"],
["7/8 (2+2+3) beaming", "t140;kick:2+2+3=x.x.x..;snare:2+2+3=..x..x.;hatClosed:2+2+3/2"],
["Son clave 2-3", "t96;claves:4/4=..x.x...x..x..x.;cowbell:4/4=x.x.x.x.x.x.x.x.;kick:4=x.x."],
["Afro 12/8 bell (try TUBS)", "t120;cowbell:4/3=x.xx.x.x.x.x;kick:4/3=X.....X.....;hatClosed:4/3=..x..x..x..x"],
["3 over 2 polyrhythm", "t90;kick:2;claves:3~"],
] }, ] },
]; ];