Skip to content

Instantly share code, notes, and snippets.

@1wheel
Last active August 22, 2016 02:20
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 1wheel/8683329 to your computer and use it in GitHub Desktop.
Save 1wheel/8683329 to your computer and use it in GitHub Desktop.
circular-synth
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<svg id='synth'></svg>
<!-- <script src="d3.js"></script> -->
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
//helper functions
var f = function(str){ return function(obj){ return obj[str]; }}
var compose = function(g, h){ return function(d){ return g(h(d)); }}
var width = 750,
height = 750,
numBeats = 16,
pitches = [130.81, 146.83, 164.81, 174.61, 196.00, 220.00, 246.94, 261.63, 146.83*2, 164.81*2, 174.61*2, 196.00*2, 220.00*2, 246.94*2, 261.63*2].reverse();
var heightScale = d3.scale.linear()
.domain([0, pitches.length])
.range([100, height/2]);
var rotationScale = d3.scale.linear()
.domain([0, numBeats - 1])
.range([360/numBeats, 360]);
var arc = d3.svg.arc()
.innerRadius(function(d, i){ return heightScale(i); })
.outerRadius(function(d, i){ return heightScale(i + 1) - 1; })
.startAngle(0)
.endAngle(2*Math.PI/numBeats - .005)
var color = d3.scale.ordinal()
.domain(d3.range(4))
.range(['white', 'steelblue', 'red', 'green']);
var svg = d3.select('#synth')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(' + [width/2, height/2] +')');
var beats = svg.selectAll('g')
.data(d3.range(numBeats)).enter()
.append('g')
.attr('transform', function(d){ return 'rotate(' + rotationScale(d) + ')'; })
var notes = beats.selectAll('circle')
.data(function(){ return pitches.map(function(d, i){ return {pitch: d, lockon: 0}; }); }).enter()
.append('path')
.attr('d', arc)
.on('click', function(d){
d.lockon = (d.lockon + 1) % 4;
d.on = d.lockon;
console.log(d.lockon);
d3.select(this).call(colorNote);
})
.on('mouseover', function(d){
if (d.lockon){ return ; }
d.on = 1;
d3.select(this)
.style('stroke', 'lightgrey')
.transition().duration(100)
.style('fill', 'steelblue')
})
.on('mouseout', function(d){
d.on = d.lockon;
d3.select(this)
.style('stroke', 'lightgrey')
.transition().duration(1000)
.call(colorNote)
})
.style('stroke-width', 2)
.style('stroke', 'lightgrey')
.style('fill', 'white')
function colorNote(selection){
selection.style('fill', function(d){ return color(d.lockon); });
}
var ac = new webkitAudioContext();
ac.createGainNode();
function osc(pitch, waveform){
oscillator = ac.createOscillator(),
oscillator.type = waveform;
oscillator.frequency.value = pitch;
gainNode = ac.createGainNode();
oscillator.connect(gainNode);
gainNode.connect(ac.destination);
gainNode.gain.value = .4;
return {osc: oscillator, gain: gainNode};
};
var nextBeat = 0;
var nextBeatTime = ac.currentTime;
setInterval(function(){
//console.log([nextBeatTime, ac.currentTime]);
while (nextBeatTime < ac.currentTime + .1){
beats.filter(function(d, i){ return i == nextBeat; })
.selectAll('path')
.each(function(d){
if (d.on){ //play pitch if not is on
var o = osc(d.pitch, d.on);
o.osc.start(nextBeatTime);
o.osc.stop(nextBeatTime + .1)
}
var selection = d3.select(this).style('stroke', 'purple')
setTimeout(function(){
selection.style('stroke', 'lightgrey');
}, 250)
})
// .transition().delay((nextBeatTime - ac.currentTime)*1000).duration(100)
// .style('stroke', 'purple')
// .transition().duration(500)
// .style('stroke', 'lightgrey')
//update time and index for the next beat
nextBeatTime += 60/240; //120 BPM
nextBeat = (nextBeat + 1) % numBeats;
}
}, 25)
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment