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:
parent
ca2a695f4f
commit
4b53f917f4
3 changed files with 48 additions and 55 deletions
|
|
@ -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">𝄞</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="";
|
||||||
|
|
|
||||||
17
mobile.html
17
mobile.html
|
|
@ -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 & library" aria-label="Save and library">💾</div>
|
<div class="icon" id="saveBtn" title="Save & 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(){
|
||||||
|
|
|
||||||
|
|
@ -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~"],
|
|
||||||
] },
|
] },
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue