-
-
Save benwilhelm/3d79643a3c061be65b482506fc6af0de to your computer and use it in GitHub Desktop.
Banjo with UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Banjo extends EventTarget { | |
muted = false | |
strings = [ | |
new BanjoString({ basePitch: 62 }), | |
new BanjoString({ basePitch: 59 }), | |
new BanjoString({ basePitch: 55 }), | |
new BanjoString({ basePitch: 50 }), | |
new BanjoString({ basePitch: 67 }) | |
] | |
fretString(stringIndex, fret) { | |
if (stringIndex === 4 && fret > 0 && fret < 6) { | |
throw new Error (`Fret ${fret} does not exist on stringIndex 4`) | |
} | |
const fretPosition = (stringIndex === 4 && fret !== 0) | |
? fret - 5 | |
: fret | |
this.strings[stringIndex].fret(fretPosition) | |
} | |
pickString(stringIndex, strength) { | |
try { | |
const tone = this.strings[stringIndex].pick(strength) | |
this.dispatchEvent(new CustomEvent('tone', { detail: tone })) | |
return { | |
...tone, | |
stringIndex, | |
volume: this.muted ? tone.volume / 2 : tone.volume | |
} | |
} catch (err) { | |
console.warn(err.message) | |
return null | |
} | |
} | |
} | |
class BanjoString { | |
fretPosition = 0 | |
broken = false | |
constructor(opts = { basePitch: 0 }) { | |
this.basePitch = opts.basePitch | |
} | |
fret(fretPosition) { | |
this.fretPosition = fretPosition | |
} | |
pick(strength) { | |
// if string is already broken, throw early | |
if (this.broken) { | |
throw new Error("Can't pick a broken string") | |
} | |
// correlate chance of breakage to strength of pick | |
const breakChance = strength * .00001 | |
if (breakChance > Math.random()) { | |
this.broken = true | |
throw new Error("String Broke") | |
} | |
return { | |
pitch: this.basePitch + this.fretPosition, | |
volume: strength, | |
duration: strength * strength | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"> | |
<link rel="stylesheet" href="./style.css"> | |
<title>Banjo</title> | |
</head> | |
<body> | |
<div class="container"> | |
<p> | |
<button class="btn btn-primary" id="play">Initialize Synth</button> | |
</p> | |
<div class="string string-4"> | |
<input type="radio" name="fret-4" checked value="0" class="fret open" data-string="4"> | |
<input type="radio" name="fret-4" value="1" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="2" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="3" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="4" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="5" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="6" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="7" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="8" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="9" class="fret" data-string="4"> | |
<input type="radio" name="fret-4" value="10" class="fret" data-string="4"> | |
<input type="range" class="strength" min="0" max="100" step="1" data-string="4" name="strength-4" /> | |
<button type="button" class="btn btn-primary pick" data-string="4" name="pick-4">Pick</button> | |
</div> | |
<div class="string string-3"> | |
<input type="radio" name="fret-3" checked value="0" class="fret open" data-string="3"> | |
<input type="radio" name="fret-3" value="1" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="2" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="3" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="4" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="5" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="6" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="7" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="8" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="9" class="fret" data-string="3"> | |
<input type="radio" name="fret-3" value="10" class="fret" data-string="3"> | |
<input type="range" class="strength" min="0" max="100" step="1" data-string="3" name="strength-3" /> | |
<button type="button" class="btn btn-primary pick" data-string="3" name="pick-3">Pick</button> | |
</div> | |
<div class="string string-2"> | |
<input type="radio" name="fret-2" checked value="0" class="fret open" data-string="2"> | |
<input type="radio" name="fret-2" value="1" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="2" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="3" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="4" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="5" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="6" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="7" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="8" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="9" class="fret" data-string="2"> | |
<input type="radio" name="fret-2" value="10" class="fret" data-string="2"> | |
<input type="range" class="strength" min="0" max="100" step="1" data-string="2" name="strength-2" /> | |
<button type="button" class="btn btn-primary pick" data-string="2" name="pick-2">Pick</button> | |
</div> | |
<div class="string string-1"> | |
<input type="radio" name="fret-1" checked value="0" class="fret open" data-string="1"> | |
<input type="radio" name="fret-1" value="1" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="2" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="3" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="4" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="5" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="6" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="7" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="8" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="9" class="fret" data-string="1"> | |
<input type="radio" name="fret-1" value="10" class="fret" data-string="1"> | |
<input type="range" class="strength" min="0" max="100" step="1" data-string="1" name="strength-1" /> | |
<button type="button" class="btn btn-primary pick" data-string="1" name="pick-1">Pick</button> | |
</div> | |
<div class="string string-0"> | |
<input type="radio" name="fret-0" checked value="0" class="fret open" data-string="0"> | |
<input type="radio" name="fret-0" value="1" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="2" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="3" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="4" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="5" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="6" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="7" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="8" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="9" class="fret" data-string="0"> | |
<input type="radio" name="fret-0" value="10" class="fret" data-string="0"> | |
<input type="range" class="strength" min="0" max="100" step="1" data-string="0" name="strength-0" /> | |
<button type="button" class="btn btn-primary pick" data-string="0" name="pick-0">Pick</button> | |
</div> | |
</div> | |
<script src="./banjo.js" charset="utf-8"></script> | |
<script src="./ui.js" charset="utf-8"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.string { | |
margin: .5em 0; | |
} | |
.fret.open { | |
opacity: .1; | |
} | |
input.fret[name='fret-4'][value='1'], | |
input.fret[name='fret-4'][value='2'], | |
input.fret[name='fret-4'][value='3'], | |
input.fret[name='fret-4'][value='4'], | |
input.fret[name='fret-4'][value='5'] { | |
visibility: hidden; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const banjo = new Banjo() | |
banjo.addEventListener('tone', tone => { | |
console.log(tone.detail) | |
}) | |
const pickButtons = document.querySelectorAll("button.pick") | |
const handlePick = (evt) => { | |
// extract the data-string attribute and parse it to a number | |
const stringIndex = +evt.target.dataset.string | |
// this is the document selector for the corresponding strength input | |
const selector = `input[name='strength-${stringIndex}']` | |
// get the value of the strenth input and parse to a number | |
const strength = +document.querySelector(selector).value | |
// Keep those UI handlers thin! | |
banjo.pickString(stringIndex, strength) | |
} | |
pickButtons.forEach(pick => pick.addEventListener('click', handlePick)) | |
const frets = document.querySelectorAll("input.fret") | |
const handleFret = (evt) => { | |
const stringIndex = +evt.target.dataset.string | |
const fret = +evt.target.value | |
banjo.fretString(stringIndex, fret) | |
} | |
frets.forEach(fret => fret.addEventListener('change', handleFret)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment