Skip to content

Instantly share code, notes, and snippets.

@eridal
Last active July 18, 2018 18:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eridal/256d8c684cc7538c71a6d105cb61c52c to your computer and use it in GitHub Desktop.
Save eridal/256d8c684cc7538c71a6d105cb61c52c to your computer and use it in GitHub Desktop.
Scale Circle Generator
<!DOCTYPE html>
<title>Scale Circles Generator</title>
<meta charset=utf-8>
<style>
canvas {
border: 1px solid #eee;
}
</style>
<body>
<canvas width="500" height="500"></canvas>
<script>
// html
let canvas = document.querySelector('canvas')
let ctx = canvas.getContext('2d')
// draw
let cw = 500
let ch = 500
let ox = 0
let oy = 0
let deg = (i, scale) => {
let d = 0
for (let j = 0; j < i; j += 1) {
d += scale[j]
}
return d
}
let cd = 6
let rd = cw / 4
let cs = 2
let ng = (d) => 2 * Math.PI / 12 * (d + cd)
let rx = (d) => rd * Math.sin(ng(d)) * -1
let ry = (d) => rd * Math.cos(ng(d))
let paint = {
background() {
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, cw, ch)
},
circle() {
// circle
ctx.beginPath()
ctx.arc(ox, oy, rd, ng(0), ng(12))
ctx.strokeStyle = '#aaa'
ctx.stroke()
// lines
for (let d = 0; d < 12; d += 1) {
ctx.beginPath()
ctx.moveTo(rx(d) * 0.95,
ry(d) * 0.95)
ctx.lineTo(rx(d) * 1.05,
ry(d) * 1.05)
ctx.stroke()
}
},
scale(scale) {
// 1. build the scale
let degs = [0]
for (let i = 0, d = 0; i < scale.length - 1; i += 1) {
d += scale[i]
degs.push(d)
}
// 2. build the chords
let funcs = []
for (let i = 0; i < scale.length; i+= 1) {
let d3 = degs[(i + 2) % degs.length]
- degs[(i + 0) % degs.length]
let d5 = degs[(i + 4) % degs.length]
- degs[(i + 0) % degs.length]
if (d3 < 0) d3 += 12
if (d5 < 0) d5 += 12
let c = `${d3}/${d5}`
funcs.push(chords[c])
}
// 2. draw the path
ctx.beginPath()
for (let i = 0; i < scale.length; i += 1) {
let d = degs[(i * cs) % 7]
if (d == 0) {
ctx.moveTo(rx(d), ry(d))
} else {
ctx.lineTo(rx(d), ry(d))
}
}
ctx.closePath()
ctx.strokeStyle = '#333'
ctx.stroke()
// notes names
ctx.font = '12px sans-serif'
for (let i = 0, d = 0; i < notes.length; i += 1) {
d += scales.major[i]
}
// 3. draw the notes
ctx.fillStyle = '#333'
for (let i = 0; i < scale.length; i += 1) {
let d = deg(i, scale)
let m = deg(i, scales.major)
let a = d == m ? '' : d < m ? '♭' : '♯'
let f = funcs[i]
ctx.fillText(notes[i] + a + f, rx(d) * 1.2, ry(d) * 1.2)
}
// 4. title
ctx.fillText(scale.name, -1 * scale.name.length, 0)
}
}
// scales
let notes = [ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]
let chords = {
'3/6': '°', // dim
'3/7': 'm', // min
'4/7': '', // maj
'4/8': '+', // aug
}
let major = [ 2, 2, 1, 2, 2, 2, 1 ]
let scales = {
major: 0,
minor: 5,
ionian: 0,
dorian: 1,
phrygian: 2,
lydian: 3,
mixolydian: 4,
aeolian: 5,
locrian: 6,
}
let mode = (m) => {
let scale = []
for (let i = 0; i < major.length; i += 1) {
let d = (i + m) % major.length
scale.push(major[d])
}
return scale
}
Object
.keys(scales)
.forEach(name => {
scales[name] = mode(scales[name])
scales[name].name = name
})
function draw(scale) {
canvas.width = canvas.width
paint.background()
ctx.translate(cw / 2, ch / 2)
paint.circle()
paint.scale(scale)
}
let names = Object.keys(scales)
let r = Math.round(Math.random() * names.length) % names.length
let name = names[r]
draw(scales[name])
//draw(scales.major)
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment