When you listen to music, you might nod your head or tap along. Some drum patterns feel regular and even. Others seem to push forward or leave unexpected gaps.
Can all those differences be explained by “faster” and “slower”?
How fast the beat moves and where sounds fall are two things we can change separately. In this lesson, we will listen for the difference.
This is the first lesson in Music Foundations. You do not need an instrument or any experience reading music. By the end, you should be able to compare two short rhythms and explain whether their tempo or their hit positions changed.
Listen first: which pattern feels more varied?
Set your device to a comfortable volume, then listen to A and B. Each clip is short and stops automatically. Notice your first impression: which feels more regular, and which feels more uneven or bouncy? You can continue even if the difference seems subtle.
Preparing the visual
Listen first, then see where the hits fall
A and B both play two bars at 100 beats per minute. Each bar has four hits: A plays on beats 1, 2, 3 and 4; B plays on beat 1, halfway after beat 2, on beat 3 and halfway after beat 4. Tone, volume and total length stay the same; the hit positions change.
{"id":"music-rhythm-compare","title":"Listen first, then see where the hits fall","summary":"A and B both play two bars at 100 beats per minute. Each bar has four hits: A plays on beats 1, 2, 3 and 4; B plays on beat 1, halfway after beat 2, on beat 3 and halfway after beat 4. Tone, volume and total length stay the same; the hit positions change.","height":440,"html":"<p class=\"eyebrow\" data-i18n=\"eyebrow\"></p><h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p>\n<div class=\"actions\"><button class=\"audio-play\" data-listen=\"a\" aria-pressed=\"false\" data-i18n=\"a\"></button><button class=\"audio-play\" data-listen=\"b\" aria-pressed=\"false\" data-i18n=\"b\"></button><button id=\"stop\" disabled data-i18n=\"stop\"></button></div>\n<div class=\"options\"><label><input id=\"reference\" type=\"checkbox\"><span data-i18n=\"reference\"></span></label><button id=\"reveal\" aria-expanded=\"false\" data-i18n=\"reveal\"></button></div>\n<div id=\"comparison\" hidden><div class=\"readout\"><strong>100 <span>BPM</span></strong><span data-i18n=\"fixed\"></span></div><p class=\"row-title\">A <span data-i18n=\"a_hint\"></span></p><div class=\"grid\" id=\"grid-a\" data-track=\"a\"></div><p class=\"row-title\">B <span data-i18n=\"b_hint\"></span></p><div class=\"grid\" id=\"grid-b\" data-track=\"b\"></div><p class=\"hint\" data-i18n=\"legend\"></p></div><p id=\"status\" class=\"status\" role=\"status\" aria-live=\"polite\" data-i18n=\"idle\"></p><div class=\"volume\"><label for=\"volume\" data-i18n=\"volume\"></label><input id=\"volume\" type=\"range\" min=\"0\" max=\"100\" value=\"65\" data-i18n-label=\"volume\"></div>","css":"\nbody {padding:24px;} h2 {font-size:21px;} .intro {color:var(--muted);margin:8px 0 18px;}\nbutton,label,.hint {font-size:16px;} button {min-height:46px;} .actions {display:flex;gap:8px;flex-wrap:wrap;margin:16px 0;}\n.actions button {flex:1 1 90px;} .audio-play::before {content:'';display:inline-block;width:10px;height:11px;background:currentColor;clip-path:polygon(0 0,100% 50%,0 100%);margin-right:5px;} .audio-play[aria-pressed=true]::before {clip-path:none;}\n.readout {display:flex;gap:12px;justify-content:space-between;align-items:baseline;padding:12px 14px;border-bottom:1px solid var(--rule);}\n.readout strong {font-size:28px;font-weight:500;} .readout span {color:var(--muted);font-size:14px;}\n.grid {display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:6px;margin:14px 0;}\n.cell {min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:7px 2px;border:1px solid var(--rule);border-radius:7px;min-height:66px;font-size:14px;}\n.cell b {font-size:21px;line-height:1;} .cell small {font-size:13px;color:var(--muted);} .cell.on {background:var(--accent-soft);border-color:var(--accent);color:var(--accent);}\n.cell.current {outline:2px solid var(--ink);outline-offset:1px;} .grid button.cell {font-size:14px;}\n.row-title {margin-top:18px;font-size:16px;font-weight:600;display:flex;justify-content:space-between;}\n.row-title span {font-size:14px;font-weight:400;color:var(--muted);} .status {min-height:3.4em;margin:14px 0 0;font-size:15px;line-height:1.7;color:var(--muted);}\n.options {display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin:14px 0;} .options label {display:flex;align-items:center;gap:8px;min-height:44px;}\ninput[type=checkbox] {width:19px;height:19px;accent-color:var(--accent);} .volume {display:flex;align-items:center;gap:12px;border-top:1px solid var(--rule);padding-top:10px;margin-top:14px;}\n.volume label {font-size:14px;color:var(--muted);white-space:nowrap;} .volume input {max-width:140px;} .tempo {margin:14px 0;} .tempo label {display:flex;justify-content:space-between;}\n.feedback {padding:14px;background:var(--surface);border-radius:8px;margin-top:12px;line-height:1.7;font-size:16px;} .choices {display:flex;gap:7px;flex-wrap:wrap;} .choices button {flex:1 1 90px;}\n@media(max-width:420px) {body {padding:20px 14px;} .grid {grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;} .cell {min-height:58px;} h2 {font-size:20px;} .readout strong {font-size:25px;}}\n","js":"\nconst $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)];\nconst patterns={a:[0,2,4,6],b:[0,3,4,7]};\nlet playingKey='', bpm=100;\nconst beatLabels=['1','&','2','&','3','&','4','&'];\nfunction grid(el, pattern, editable=false) {\n el.replaceChildren();\n for(let i=0;i<8;i++) {\n const cell=document.createElement(editable?'button':'div'); cell.className='cell'+(pattern.includes(i)?' on':''); cell.dataset.slot=i;\n if(editable) {cell.type='button';cell.setAttribute('aria-pressed',String(pattern.includes(i)));}\n cell.setAttribute('aria-label',viz.t('beat')+' '+(Math.floor(i/2)+1)+(i%2?viz.t('half'):'')+' · '+viz.t(pattern.includes(i)?'hit':'rest'));\n const label=document.createElement('small');label.textContent=beatLabels[i];\n const mark=document.createElement('b');mark.textContent=pattern.includes(i)?'●':'—';mark.setAttribute('aria-hidden','true');cell.append(label,mark);el.append(cell);\n }\n}\nfunction makeScore(pattern, tempo, reference=false) {\n const events=[];\n for(let bar=0;bar<2;bar++) {\n for(const step of pattern) events.push({beat:bar*4+step/2,hz:740,duration:.075,gain:1,wave:'triangle'});\n if(reference) for(let beat=0;beat<4;beat++) events.push({beat:bar*4+beat,hz:1500,duration:.035,gain:.45});\n }\n return {bpm:tempo,beats:8,events};\n}\nconst player=viz.audio({\n onState(state) {\n document.body.dataset.audioState=state;\n $$('[data-listen]').forEach(b=>b.setAttribute('aria-pressed',String(state!=='idle'&&b.dataset.listen===playingKey)));\n $('#stop').disabled=state==='idle';\n $('#status').textContent=viz.t(state==='playing'?'playing':state==='starting'?'starting':'idle');\n },\n onTick(beat) {\n const step=beat<0?-1:Math.floor(beat*2)%8;\n $$('.cell').forEach(c=>c.classList.toggle('current',beat>=0&&Number(c.dataset.slot)===step&&(!c.closest('[data-track]')||c.closest('[data-track]').dataset.track===playingKey)));\n },\n onError() {$('#status').textContent=viz.t('error');}\n});\n$('#stop').onclick=()=>player.stop();\n$('#volume').oninput=e=>player.setVolume(Number(e.target.value)/100);\nfunction listen(key,pattern,tempo,reference=false) {playingKey=key;return player.play(makeScore(pattern,tempo,reference));}\n\nfor(const key of ['a','b']) {grid($('#grid-'+key),patterns[key]);$('[data-listen='+key+']').onclick=()=>listen(key,patterns[key],100,$('#reference').checked);}\n$('#reference').onchange=()=>player.stop();\n$('#reveal').onclick=()=>{const expanded=$('#comparison').hidden;$('#comparison').hidden=!expanded;$('#reveal').setAttribute('aria-expanded',String(expanded));$('#reveal').textContent=viz.t(expanded?'hide':'reveal');};\n","audio":true,"strings":{"a":"Listen to A","a_hint":"A hit on every beat","b":"Listen to B","b_hint":"Two hits fall between beats","beat":"Beat","error":"Audio could not start. Check that your device is unmuted, then try again. You can also read the diagram.","eyebrow":"01 / LISTEN","fixed":"Same tempo in both","half":", halfway to the next beat","heading":"Do A and B feel equally fast?","hide":"Hide the diagram","hit":"hit","idle":"Each clip plays two bars. Stop or listen again at any time.","intro":"Listen to A and B separately. Notice how you want to nod or clap along. Start with your impression.","legend":"● means a hit; — means no hit. & marks the halfway point between beats.","playing":"Playing two bars. Playback stops at the end.","reference":"Add a steady pulse","rest":"no hit","reveal":"Show the timing","starting":"Preparing audio…","stop":"Stop","volume":"Volume"}}
Now choose Show the timing and listen again. Both versions use 100 BPM, four hits per bar, and the same tone and volume. In B, two hits move half a beat later.
Select Add a steady pulse to hear a softer, higher-pitched sound at regular intervals. Try counting “1, 2, 3, 4” with it. Notice that the lower-pitched hits sometimes line up with the pulse and sometimes fall between its beats.
Each cell represents half a beat. ● means a hit; — means no hit. The & marks the halfway point between beats. Read it as “and”: one and two and three and four and. The pattern repeats once, giving us two bars in total.
You do not have to hear B as “faster.” First impressions can differ. What we can check is this: the reference pulse has not sped up, but some hits have moved relative to it.
Separate beat, tempo, and rhythmic pattern
Imagine walking at a steady pace. Your footsteps can serve as a beat. The pace at which you walk corresponds to tempo. Clapping on particular steps—or between steps—creates different rhythmic patterns.
Idea
What it means here
How to check it
Beat
A regular pulse we can follow through the music
Count along with the reference pulse
Tempo
How quickly that pulse moves
Compare the time between beats
Rhythmic pattern
How sounds and gaps are arranged in time
Notice which hits fall on beats and which fall between them
BPM means beats per minute. Using the beat unit chosen for this lesson, 60 BPM gives one beat per second; 120 BPM gives one every half second; 100 BPM gives one every 0.6 seconds. BPM does not count every sound you hear. A beat can contain two hits, or no hit at all. Beat, tempo, and meter
Here, we group four beats into a bar, also called a measure, to make comparisons easy. Music does not always group beats in fours. We will explore how beats and accents form groups in a later lesson.
Rhythm in real music also involves sound lengths, accents, and details of performance. For now, we keep the tone and the short, percussive sound fixed and focus on when each hit begins.
Try it: move a hit, then change the tempo
Start with one small edit. Keep 100 BPM, and move the second hit in pattern A from “2” to the “&” immediately after it. Listen again. Can you keep counting at the same pace?
Next, restore A. Leave the cells unchanged and compare 80, 100, and 120 BPM. This time the reference pulse speeds up too, and the whole clip takes less time.
Preparing the visual
Keep one thing fixed and change another
Each of the four beats in a bar is divided into two cells, and the pattern plays twice. Restore A or B, then keep the tempo at 100 BPM while changing hit positions. Or keep the pattern fixed and compare 80, 100 and 120 BPM. Editing stops playback; the next listen starts from the beginning.
{"id":"music-rhythm-workbench","title":"Keep one thing fixed and change another","summary":"Each of the four beats in a bar is divided into two cells, and the pattern plays twice. Restore A or B, then keep the tempo at 100 BPM while changing hit positions. Or keep the pattern fixed and compare 80, 100 and 120 BPM. Editing stops playback; the next listen starts from the beginning.","height":650,"html":"<p class=\"eyebrow\" data-i18n=\"eyebrow\"></p><h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p>\n<div class=\"grid\" id=\"edit-grid\"></div><p class=\"hint\" data-i18n=\"legend\"></p>\n<div class=\"actions\"><button data-pattern=\"a\" data-i18n=\"restore_a\"></button><button data-pattern=\"b\" data-i18n=\"restore_b\"></button></div>\n<div class=\"tempo\"><label for=\"tempo\"><span data-i18n=\"tempo\"></span><output id=\"tempo-value\">100 BPM</output></label><input id=\"tempo\" type=\"range\" min=\"60\" max=\"140\" step=\"1\" value=\"100\" data-i18n-label=\"tempo\"></div>\n<div class=\"choices\"><button data-tempo=\"80\">80 BPM</button><button data-tempo=\"100\">100 BPM</button><button data-tempo=\"120\">120 BPM</button></div>\n<div class=\"options\"><label><input id=\"reference\" type=\"checkbox\" checked><span data-i18n=\"reference\"></span></label></div>\n<div class=\"actions\"><button class=\"audio-play primary\" data-listen=\"edit\" aria-pressed=\"false\" data-i18n=\"listen\"></button><button id=\"stop\" disabled data-i18n=\"stop\"></button></div><p class=\"hint\" id=\"configuration\"></p><p id=\"status\" class=\"status\" role=\"status\" aria-live=\"polite\" data-i18n=\"idle\"></p><div class=\"volume\"><label for=\"volume\" data-i18n=\"volume\"></label><input id=\"volume\" type=\"range\" min=\"0\" max=\"100\" value=\"65\" data-i18n-label=\"volume\"></div>","css":"\nbody {padding:24px;} h2 {font-size:21px;} .intro {color:var(--muted);margin:8px 0 18px;}\nbutton,label,.hint {font-size:16px;} button {min-height:46px;} .actions {display:flex;gap:8px;flex-wrap:wrap;margin:16px 0;}\n.actions button {flex:1 1 90px;} .audio-play::before {content:'';display:inline-block;width:10px;height:11px;background:currentColor;clip-path:polygon(0 0,100% 50%,0 100%);margin-right:5px;} .audio-play[aria-pressed=true]::before {clip-path:none;}\n.readout {display:flex;gap:12px;justify-content:space-between;align-items:baseline;padding:12px 14px;border-bottom:1px solid var(--rule);}\n.readout strong {font-size:28px;font-weight:500;} .readout span {color:var(--muted);font-size:14px;}\n.grid {display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:6px;margin:14px 0;}\n.cell {min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:7px 2px;border:1px solid var(--rule);border-radius:7px;min-height:66px;font-size:14px;}\n.cell b {font-size:21px;line-height:1;} .cell small {font-size:13px;color:var(--muted);} .cell.on {background:var(--accent-soft);border-color:var(--accent);color:var(--accent);}\n.cell.current {outline:2px solid var(--ink);outline-offset:1px;} .grid button.cell {font-size:14px;}\n.row-title {margin-top:18px;font-size:16px;font-weight:600;display:flex;justify-content:space-between;}\n.row-title span {font-size:14px;font-weight:400;color:var(--muted);} .status {min-height:3.4em;margin:14px 0 0;font-size:15px;line-height:1.7;color:var(--muted);}\n.options {display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin:14px 0;} .options label {display:flex;align-items:center;gap:8px;min-height:44px;}\ninput[type=checkbox] {width:19px;height:19px;accent-color:var(--accent);} .volume {display:flex;align-items:center;gap:12px;border-top:1px solid var(--rule);padding-top:10px;margin-top:14px;}\n.volume label {font-size:14px;color:var(--muted);white-space:nowrap;} .volume input {max-width:140px;} .tempo {margin:14px 0;} .tempo label {display:flex;justify-content:space-between;}\n.feedback {padding:14px;background:var(--surface);border-radius:8px;margin-top:12px;line-height:1.7;font-size:16px;} .choices {display:flex;gap:7px;flex-wrap:wrap;} .choices button {flex:1 1 90px;}\n@media(max-width:420px) {body {padding:20px 14px;} .grid {grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;} .cell {min-height:58px;} h2 {font-size:20px;} .readout strong {font-size:25px;}}\n","js":"\nconst $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)];\nconst patterns={a:[0,2,4,6],b:[0,3,4,7]};\nlet playingKey='', bpm=100;\nconst beatLabels=['1','&','2','&','3','&','4','&'];\nfunction grid(el, pattern, editable=false) {\n el.replaceChildren();\n for(let i=0;i<8;i++) {\n const cell=document.createElement(editable?'button':'div'); cell.className='cell'+(pattern.includes(i)?' on':''); cell.dataset.slot=i;\n if(editable) {cell.type='button';cell.setAttribute('aria-pressed',String(pattern.includes(i)));}\n cell.setAttribute('aria-label',viz.t('beat')+' '+(Math.floor(i/2)+1)+(i%2?viz.t('half'):'')+' · '+viz.t(pattern.includes(i)?'hit':'rest'));\n const label=document.createElement('small');label.textContent=beatLabels[i];\n const mark=document.createElement('b');mark.textContent=pattern.includes(i)?'●':'—';mark.setAttribute('aria-hidden','true');cell.append(label,mark);el.append(cell);\n }\n}\nfunction makeScore(pattern, tempo, reference=false) {\n const events=[];\n for(let bar=0;bar<2;bar++) {\n for(const step of pattern) events.push({beat:bar*4+step/2,hz:740,duration:.075,gain:1,wave:'triangle'});\n if(reference) for(let beat=0;beat<4;beat++) events.push({beat:bar*4+beat,hz:1500,duration:.035,gain:.45});\n }\n return {bpm:tempo,beats:8,events};\n}\nconst player=viz.audio({\n onState(state) {\n document.body.dataset.audioState=state;\n $$('[data-listen]').forEach(b=>b.setAttribute('aria-pressed',String(state!=='idle'&&b.dataset.listen===playingKey)));\n $('#stop').disabled=state==='idle';\n $('#status').textContent=viz.t(state==='playing'?'playing':state==='starting'?'starting':'idle');\n },\n onTick(beat) {\n const step=beat<0?-1:Math.floor(beat*2)%8;\n $$('.cell').forEach(c=>c.classList.toggle('current',beat>=0&&Number(c.dataset.slot)===step&&(!c.closest('[data-track]')||c.closest('[data-track]').dataset.track===playingKey)));\n },\n onError() {$('#status').textContent=viz.t('error');}\n});\n$('#stop').onclick=()=>player.stop();\n$('#volume').oninput=e=>player.setVolume(Number(e.target.value)/100);\nfunction listen(key,pattern,tempo,reference=false) {playingKey=key;return player.play(makeScore(pattern,tempo,reference));}\n\nlet pattern=[...patterns.a];\nfunction render() {\n grid($('#edit-grid'),pattern,true);$('#tempo-value').textContent=bpm+' BPM';\n $('#configuration').textContent=viz.t('current')+bpm+' BPM · '+pattern.length+viz.t('hits');\n $$('#edit-grid button').forEach(cell=>cell.onclick=()=>{player.stop();const step=Number(cell.dataset.slot);pattern=pattern.includes(step)?pattern.filter(x=>x!==step):[...pattern,step].sort((a,b)=>a-b);render();$('#edit-grid').children[step].focus();});\n $$('[data-tempo]').forEach(b=>b.setAttribute('aria-pressed',String(Number(b.dataset.tempo)===bpm)));\n}\nfunction tempo(value) {player.stop();bpm=Number(value);$('#tempo').value=bpm;render();}\n$('#tempo').oninput=e=>tempo(e.target.value);\n$$('[data-tempo]').forEach(b=>b.onclick=()=>tempo(b.dataset.tempo));\n$$('[data-pattern]').forEach(b=>b.onclick=()=>{player.stop();pattern=[...patterns[b.dataset.pattern]];render();});\n$('[data-listen]').onclick=()=>listen('edit',pattern,bpm,$('#reference').checked);\n$('#reference').onchange=()=>player.stop();render();\n","audio":true,"strings":{"beat":"Beat","current":"Now: ","error":"Audio could not start. Check that your device is unmuted, then try again. You can also read the diagram.","eyebrow":"02 / TRY IT","half":", halfway to the next beat","heading":"Move a hit, or change the tempo","hit":"hit","hits":" hits per bar","idle":"Each clip plays two bars. Stop or listen again at any time.","intro":"Keep 100 BPM and tap cells to add or remove hits. Then keep your pattern fixed and change only the tempo. Listen again after each edit.","legend":"● means a hit; — means no hit. & marks the halfway point between beats.","listen":"Listen to my pattern","playing":"Playing two bars. Playback stops at the end.","reference":"Add a steady pulse","rest":"no hit","restore_a":"Restore pattern A","restore_b":"Restore pattern B","starting":"Preparing audio…","stop":"Stop","tempo":"Tempo","volume":"Volume"}}
These two edits do different things. Moving a hit changes its position relative to the beat. Increasing the tempo makes the same arrangement unfold more quickly.
Try leaving a gap: remove a hit on one of the beats, but keep the reference pulse on. You can still count through that moment. A beat does not disappear just because nothing is played on it. You can also turn the reference pulse off and keep counting silently. Turn it back on whenever it helps.
What if I keep following the hits and lose the steady beat?
Restore A and focus on the higher-pitched reference pulse while counting “1, 2, 3, 4.” Then switch to B and try to keep the same spacing between your counts. Slow down to 80 BPM or use the grid to find your place. This is practice in choosing which layer to follow, not a reaction-time test.
Some music allows more than one way of feeling the beat. Listeners may tap at half or twice one another’s rate. Our experiment specifies a reference pulse so that we can compare both patterns against the same timing framework.
Can you hear what changed in a new example?
The next examples use different patterns. Compare C and D in each pair. Has the reference pulse sped up, or have the lower-pitched hits moved? Listen as often as you need. Follow the reference pulse first, then shift your attention to the hits.
Preparing the visual
Try new patterns and identify what changed
In the first pair, both clips use 100 BPM but have different hit positions. In the second pair, the hit positions stay the same while the tempo changes from 90 to 120 BPM. Listen and choose an answer; the written explanation is also available below.
{"id":"music-rhythm-check","title":"Try new patterns and identify what changed","summary":"In the first pair, both clips use 100 BPM but have different hit positions. In the second pair, the hit positions stay the same while the tempo changes from 90 to 120 BPM. Listen and choose an answer; the written explanation is also available below.","height":520,"html":"<p class=\"eyebrow\" data-i18n=\"eyebrow\"></p><h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p>\n<div class=\"choices\"><button data-question=\"0\" aria-pressed=\"true\" data-i18n=\"q1\"></button><button data-question=\"1\" aria-pressed=\"false\" data-i18n=\"q2\"></button></div>\n<div class=\"actions\"><button class=\"audio-play\" data-listen=\"c\" aria-pressed=\"false\" data-i18n=\"c\"></button><button class=\"audio-play\" data-listen=\"d\" aria-pressed=\"false\" data-i18n=\"d\"></button><button id=\"stop\" disabled data-i18n=\"stop\"></button></div>\n<p class=\"intro\" data-i18n=\"prompt\"></p><div class=\"choices\"><button data-answer=\"rhythm\" data-i18n=\"rhythm\"></button><button data-answer=\"tempo\" data-i18n=\"tempo_answer\"></button><button data-answer=\"unsure\" data-i18n=\"unsure\"></button></div>\n<p id=\"feedback\" class=\"feedback\" role=\"status\" aria-live=\"polite\" data-i18n=\"initial\"></p><p id=\"status\" class=\"status\" role=\"status\" aria-live=\"polite\" data-i18n=\"idle\"></p><div class=\"volume\"><label for=\"volume\" data-i18n=\"volume\"></label><input id=\"volume\" type=\"range\" min=\"0\" max=\"100\" value=\"65\" data-i18n-label=\"volume\"></div>","css":"\nbody {padding:24px;} h2 {font-size:21px;} .intro {color:var(--muted);margin:8px 0 18px;}\nbutton,label,.hint {font-size:16px;} button {min-height:46px;} .actions {display:flex;gap:8px;flex-wrap:wrap;margin:16px 0;}\n.actions button {flex:1 1 90px;} .audio-play::before {content:'';display:inline-block;width:10px;height:11px;background:currentColor;clip-path:polygon(0 0,100% 50%,0 100%);margin-right:5px;} .audio-play[aria-pressed=true]::before {clip-path:none;}\n.readout {display:flex;gap:12px;justify-content:space-between;align-items:baseline;padding:12px 14px;border-bottom:1px solid var(--rule);}\n.readout strong {font-size:28px;font-weight:500;} .readout span {color:var(--muted);font-size:14px;}\n.grid {display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:6px;margin:14px 0;}\n.cell {min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:7px 2px;border:1px solid var(--rule);border-radius:7px;min-height:66px;font-size:14px;}\n.cell b {font-size:21px;line-height:1;} .cell small {font-size:13px;color:var(--muted);} .cell.on {background:var(--accent-soft);border-color:var(--accent);color:var(--accent);}\n.cell.current {outline:2px solid var(--ink);outline-offset:1px;} .grid button.cell {font-size:14px;}\n.row-title {margin-top:18px;font-size:16px;font-weight:600;display:flex;justify-content:space-between;}\n.row-title span {font-size:14px;font-weight:400;color:var(--muted);} .status {min-height:3.4em;margin:14px 0 0;font-size:15px;line-height:1.7;color:var(--muted);}\n.options {display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin:14px 0;} .options label {display:flex;align-items:center;gap:8px;min-height:44px;}\ninput[type=checkbox] {width:19px;height:19px;accent-color:var(--accent);} .volume {display:flex;align-items:center;gap:12px;border-top:1px solid var(--rule);padding-top:10px;margin-top:14px;}\n.volume label {font-size:14px;color:var(--muted);white-space:nowrap;} .volume input {max-width:140px;} .tempo {margin:14px 0;} .tempo label {display:flex;justify-content:space-between;}\n.feedback {padding:14px;background:var(--surface);border-radius:8px;margin-top:12px;line-height:1.7;font-size:16px;} .choices {display:flex;gap:7px;flex-wrap:wrap;} .choices button {flex:1 1 90px;}\n@media(max-width:420px) {body {padding:20px 14px;} .grid {grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;} .cell {min-height:58px;} h2 {font-size:20px;} .readout strong {font-size:25px;}}\n","js":"\nconst $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)];\nconst patterns={a:[0,2,4,6],b:[0,3,4,7]};\nlet playingKey='', bpm=100;\nconst beatLabels=['1','&','2','&','3','&','4','&'];\nfunction grid(el, pattern, editable=false) {\n el.replaceChildren();\n for(let i=0;i<8;i++) {\n const cell=document.createElement(editable?'button':'div'); cell.className='cell'+(pattern.includes(i)?' on':''); cell.dataset.slot=i;\n if(editable) {cell.type='button';cell.setAttribute('aria-pressed',String(pattern.includes(i)));}\n cell.setAttribute('aria-label',viz.t('beat')+' '+(Math.floor(i/2)+1)+(i%2?viz.t('half'):'')+' · '+viz.t(pattern.includes(i)?'hit':'rest'));\n const label=document.createElement('small');label.textContent=beatLabels[i];\n const mark=document.createElement('b');mark.textContent=pattern.includes(i)?'●':'—';mark.setAttribute('aria-hidden','true');cell.append(label,mark);el.append(cell);\n }\n}\nfunction makeScore(pattern, tempo, reference=false) {\n const events=[];\n for(let bar=0;bar<2;bar++) {\n for(const step of pattern) events.push({beat:bar*4+step/2,hz:740,duration:.075,gain:1,wave:'triangle'});\n if(reference) for(let beat=0;beat<4;beat++) events.push({beat:bar*4+beat,hz:1500,duration:.035,gain:.45});\n }\n return {bpm:tempo,beats:8,events};\n}\nconst player=viz.audio({\n onState(state) {\n document.body.dataset.audioState=state;\n $$('[data-listen]').forEach(b=>b.setAttribute('aria-pressed',String(state!=='idle'&&b.dataset.listen===playingKey)));\n $('#stop').disabled=state==='idle';\n $('#status').textContent=viz.t(state==='playing'?'playing':state==='starting'?'starting':'idle');\n },\n onTick(beat) {\n const step=beat<0?-1:Math.floor(beat*2)%8;\n $$('.cell').forEach(c=>c.classList.toggle('current',beat>=0&&Number(c.dataset.slot)===step&&(!c.closest('[data-track]')||c.closest('[data-track]').dataset.track===playingKey)));\n },\n onError() {$('#status').textContent=viz.t('error');}\n});\n$('#stop').onclick=()=>player.stop();\n$('#volume').oninput=e=>player.setVolume(Number(e.target.value)/100);\nfunction listen(key,pattern,tempo,reference=false) {playingKey=key;return player.play(makeScore(pattern,tempo,reference));}\n\nconst questions=[{c:[0,1,4,6],d:[0,2,5,6],tempos:[100,100],answer:'rhythm'},{c:[0,2,3,6],d:[0,2,3,6],tempos:[90,120],answer:'tempo'}];\nlet question=0;\n$$('[data-question]').forEach(b=>b.onclick=()=>{player.stop();question=Number(b.dataset.question);$$('[data-question]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));$('#feedback').textContent=viz.t('initial');});\n$$('[data-listen]').forEach(b=>b.onclick=()=>{const q=questions[question],key=b.dataset.listen;listen(key,q[key],q.tempos[key==='c'?0:1],true);});\n$$('[data-answer]').forEach(b=>b.onclick=()=>{const prefix=b.dataset.answer==='unsure'?'unsure_feedback':b.dataset.answer===questions[question].answer?'correct':'retry';$('#feedback').textContent=viz.t(prefix)+' '+viz.t('explain_'+question);});\n","audio":true,"strings":{"beat":"Beat","c":"Listen to C","correct":"That matches the example.","d":"Listen to D","error":"Audio could not start. Check that your device is unmuted, then try again. You can also read the diagram.","explain_0":"Both clips in the first pair are at 100 BPM. Each bar has four hits, but two hits move to different positions.","explain_1":"The second pair keeps the same pattern. C is at 90 BPM and D is at 120 BPM, so the pulse and the hits both speed up.","eyebrow":"03 / CHECK","half":", halfway to the next beat","heading":"What changed this time?","hit":"hit","idle":"Each clip plays two bars. Stop or listen again at any time.","initial":"Compare as often as you like. Choose an answer to see the explanation.","intro":"Both pairs include a softer, higher-pitched reference pulse. Listen for a change in its speed, then follow the lower-pitched hits.","legend":"● means a hit; — means no hit. & marks the halfway point between beats.","playing":"Playing two bars. Playback stops at the end.","prompt":"From C to D, what changed?","q1":"First pair","q2":"Second pair","reference":"Add a steady pulse","rest":"no hit","retry":"Listen to the reference pulse again.","rhythm":"Hit positions","starting":"Preparing audio…","stop":"Stop","tempo_answer":"Tempo","unsure":"Not sure yet","unsure_feedback":"Read the explanation, then listen again.","volume":"Volume"}}Read the answers for both pairs
First pair: C and D are both at 100 BPM, with four hits per bar. C plays on “1, the and after 1, 3, 4.” D plays on “1, 2, the and after 3, 4.” The tempo and number of hits stay the same; the positions change.
Second pair: both clips play on “1, 2, the and after 2, 4.” C uses 90 BPM and D uses 120 BPM. The pattern stays the same, but D’s pulse and hits move faster, so its two bars finish sooner.
If you cannot play audio right now, draw eight cells on paper and mark these positions. Check whether the positions or the BPM values change. Return to the listening exercise when you can hear the clips.
Take one observation into your next song
Choose a familiar song with a clear beat. Count along quietly, then notice the drums or the voice. Does every sound begin exactly on a beat? Where are the gaps, and where do sounds fall between beats? There is no need to calculate the song’s BPM yet.
When you want to say “this part feels more energetic,” try adding a specific observation: “The beat is still steady, but the hit positions changed,” or “The same pattern is now faster.” That extra sentence turns a broad impression into a musical relationship you can compare and discuss.
The next lesson will move from when a sound happens to which note it is: why can we still recognize a melody when every note moves higher?