Last active
July 18, 2018 18:09
-
-
Save eridal/256d8c684cc7538c71a6d105cb61c52c to your computer and use it in GitHub Desktop.
Scale Circle Generator
This file contains hidden or 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> | |
<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