The code to create and connect the knobs is in the last four lines of the script below.
Note the circular connection between the 2D pad and the sliders. JZZ.js is smart enough to not get stuck in infinite loop! :)
<!DOCTYPE html> <html> <head> <title>Knobs</title> <style type="text/css"> #msg { width:100%; height:24em; margin:0; padding:0; background-color:#eee; border:solid 1px #888; white-space:pre; font-family:Courier New, monospace; overflow:auto;} .key {position:absolute; bottom:0; left:0; width:100%; text-align:center;} #piano { position:relative; width:473px; height:151px; margin:.5em;} #keybd, #pad, #sld0, #sld1 { display:inline-block; position:absolute; margin:0; padding:0;} #keybd { left:178px;} #pad { top:0; left:29px;} #sld1 { top:117px; left:29px;} #sld2 { top:0; left:0;} </style> <script src="JZZ.js"></script> <script src="JZZ.synth.Tiny.js"></script> <script src="JZZ.input.Kbd.js"></script> </head> <body> <h1>Knobs</h1> <div id=piano> <span id=pad></span> <span id=sld1></span> <span id=sld2></span> <span id=keybd></span> </div> <div id=msg></div> <script><!-- var msgarea = document.getElementById('msg'); var text = []; function print(msg) { text.push(JZZ.MIDI(msg).toString()); if (text.length > 20) text = text.slice(1); msgarea.innerHTML = text.join('<br>'); msgarea.scrollTop = msgarea.scrollHeight; } JZZ.synth.Tiny.register('Web Audio'); var out = JZZ().openMidiOut(); var piano = JZZ.input.Kbd({at:'keybd', from:'C5', to:'B5', onCreate:function(){ this.getBlackKeys().setStyle({color:'#fff'}); this.getKey('C5').setInnerHTML('<span class=key>Z</span>'); this.getKey('C#5').setInnerHTML('<span class=key>S</span>'); this.getKey('D5').setInnerHTML('<span class=key>X</span>'); this.getKey('D#5').setInnerHTML('<span class=key>D</span>'); this.getKey('E5').setInnerHTML('<span class=key>C</span>'); this.getKey('F5').setInnerHTML('<span class=key>V</span>'); this.getKey('F#5').setInnerHTML('<span class=key>G</span>'); this.getKey('G5').setInnerHTML('<span class=key>B</span>'); this.getKey('G#5').setInnerHTML('<span class=key>H</span>'); this.getKey('A5').setInnerHTML('<span class=key>N</span>'); this.getKey('A#5').setInnerHTML('<span class=key>J</span>'); this.getKey('B5').setInnerHTML('<span class=key>M</span>'); }}).connect(out).connect(print); JZZ.input.ASCII({Z:'C5', S:'C#5', X:'D5', D:'D#5', C:'E5', V:'F5', G:'F#5', B:'G5', H:'Ab5', N:'A5', J:'Bb5', M:'B5'}).connect(piano); var pad = JZZ.input.Pad({at:'pad', rh:96, kw:16}).connect(piano); var slider1 = JZZ.input.Slider({at:'sld1', pos:'E'}).connect(pad); var slider2 = JZZ.input.Slider({at:'sld2', data:'mod', rh:96}).connect(pad); pad.connect(slider1).connect(slider2); --></script> </body> </html>