<!-- AUDIOLET.JS by OAMPO
https://raw.github.com/oampo/Audiolet/master/LICENSE -->

<!-- JQUERY.KNOBBY.JS --->

	<div class="keyboard">
    <div class="key c zero"><k id="germanY">Y</k></div>
		<div class="key c cross zero"><k>S</k></div>
		<div class="key d zero"><k>X</k></div>
		<div class="key d cross zero"><k>D</k></div>
		<div class="key e zero"><k>C</k></div>
		<div class="key f zero"><k>V</k></div>
		<div class="key f cross zero"><k>G</k></div>
		<div class="key g zero"><k>B</k></div>
		<div class="key g cross zero"><k>H</k></div>
		<div class="key a zero"><k>N</k></div>
		<div class="key a cross zero"><k>J</k></div>
		<div class="key b zero"><k>M</k></div>
		<div class="key c one"><k>Q</k></div>
    <div class="key c cross one"><k>2</k></div>
    <div class="key d one"><k>W</k></div>
		<div class="key d cross one"><k>3</k></div>
		<div class="key e one"><k>E</k></div>
    <div class="key f one"><k>R</k></div>
		<div class="key f cross one"><k>5</k></div>
		<div class="key g one"><k>T</k></div>
		<div class="key g cross one"><k>6</k></div>
		<div class="key a one"><k id="germanZ">Z</k></div>
    <div class="key a cross one"><k>7</k></div>
		<div class="key b one"><k>U</k></div>
		<div class="key c two"><k>I</k></div>
		<div class="key c cross two"><k>9</k></div>
		<div class="key d two"><k>O</k></div>
		<div class="key d cross two"><k>0</k></div>
		<div class="key e two"><k>P</k></div>
		<div class="key f two"><k>&Uuml;</k></div>
		<div class="key f cross two"></div>
		<div class="key g two"></div>
		<div class="key g cross two"></div>
		<div class="key a two"></div>
		<div class="key a cross two"></div>
		<div class="key b two"></div>
		<div class="key c three"></div>
		<div class="key c cross three"></div>
		<div class="key d three"></div>
		<div class="key d cross three"></div>
		<div class="key e three"></div>
		<div class="key f three"></div>
		<div class="key f cross three"></div>
		<div class="key g three"></div>
		<div class="key g cross three"></div>
		<div class="key a three"></div>
		<div class="key a cross three"></div>
		<div class="key b three"></div>
		<div class="key c four"></div>
		
	</div>
<fieldset>
	<legend>Keyboard</legend>
  <label>Keyboard-Layout: 
		<select name="keyboardLayout">
			<option value="de">German (QWERTZ)</option>
			<option value="en">English (QWERTY)</option>
<!--<option value="FRE">French (AZERTY)</option>-->
	  </select>
  </label>
	<label>
      <input type=checkbox name=showKeyboardKeys checked /> Show keys   
   </label>
	<label>
      <input type=checkbox name=transposeUp /> Transpose 1 oct up
    </label>
</label>
</fieldset>
<fieldset><legend>Pass</legend>
<label>
  LOW
  <input class=number type=number name=lowPass value=200 min=1 max=10000 turn=2 step=10>
</label>
<label>
  HIGH
	<input class=number type=number name=highPass value=300 min=1 max=10000 turn=2 step=10>
</label>
</fieldset>

<fieldset><legend><label><input name=enableDelay type="checkbox" checked> Delay</label></legend>
		<label>
		  Time (sec)
		  <input class=number type=number name=delayTime step=0.01 value=0.3 max=1 min=0.01> 
		</label>
		<label>
		  Feedback
		  <input class=number type=number name=delayFeedback step=0.01 value=0.5 max=1 min=0>
		</label>
		<label>
		  Mix
		  <input class=number type=number name=delayMix step=0.01 max=1 min=0 value=0.5 >
		</label>
</fieldset>
.knobby-wrap {
  padding: 5px;
  display: inline-block;
  vertical-align: middle;
  text-align: center;
}

.knobby-knob {
  display: block;
  position: relative;
  width: 8em;
  height: 8em;
  border-radius: 100%;
  overflow: hidden;
  /* created with http://codeblock.at/base64-noise-generator/ */
  background: #212121 url(data:image/png;base64,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);
  background-size: 50% 50%;
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.5);
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}

.knobby-shadow {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;
  border-radius: 100%;
  box-shadow: 0 -1px 6px rgba(0, 0, 0, 0.5) inset, 0 1px 3px rgba(255, 255, 255, 0.85) inset;
}

.knobby-handle {
  transform: rotate(0deg) translateY(-3em);
  position: absolute;
  width: 1em;
  height: 1em;
  left: 50%;
  top: 50%;
  margin-top: -0.5em;
  margin-left: -0.5em;
  background: #525b58;
  border-radius: 100%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5) inset, 0 -2px 4px rgba(255, 255, 255, 0.85) inset;
  pointer-events: none;
}

.knobby-input {
  margin: 4px auto;
  text-align: right;
  background: #222;
  color: #eee;
  border: 1px solid #111;
  padding: 5px 0;
  font-family: monospace;
  box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.5), 0 1px 1px rgba(255, 255, 255, 0.85);
}

@keyWidth: 50px;
@keyHeight: 200px;
@crossHeightRatio: 0.75;
@crossWidthRatio: 0.66;
@import url(https://fonts.googleapis.com/css?family=Roboto);
body {
  -webkit-backface-visibility: hidden;
  font: 0.9em "Roboto", sans-serif;
  line-height: 2.0;
  background: #333;
  color: #aaa;
}

fieldset {
  display: block;
  border: 1px solid gray;
}

div.keyboard {
  -webkit-perspective: 1000px;
  -moz-perspective: 1000px;
  perspective: 1000px;
  margin: 10px auto;
  height: 220px;
  width:1450px;
}

label { padding: 0 1em }

.key {
  position: absolute;
  background: white;
  height: @keyHeight;
  width: @keyWidth;
  box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  border-radius: 0 0 5px 5px;
  -webkit-transform-origin: top center;
  -webkit-transform: translate(0, 0) rotateX(0);
  -moz-transform-origin: top center;
  -moz-transform: translate(0, 0) rotateX(0);
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  -webkit-transition: all .2s ease;
  -moz-transition: all .2s ease;
  transition: all .2s ease;
  border: 1px solid gray;
  z-index: 90;
  k {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 16px;
    height: 16px;
    line-height: 16px;
    text-align: center;
    color: gray;
    font-size: 10px;
    font-weight: bold;
    opacity: 0.8;
  }
}

body.hideKeyboardKeys .key k {
  display: none;
}

.key.active {
  //background: orange!important;
  
  //-webkit-transform-origin:top center;
  -webkit-transform: translate3d(0, 0, -5px) rotateX(-1deg) scale(0.95);
  //-moz-transform-origin:top center;
  -moz-transform: rotateX(-1deg) scale(0.95);
  z-index: 80;
  
  k {
    //color:white;
  }
}

.key.cross.active {
  z-index: 100;
  -webkit-transform: rotateX(0) scale(0.95);
  //-moz-transform-origin:top center;
  -moz-transform: rotateX(0) scale(0.95);
}

.key.cross {
  height: @keyHeight * @crossHeightRatio;
  background: black;
  margin-left: @keyWidth - (@keyWidth * @crossWidthRatio/2);
  z-index: 100;
  width: @keyWidth * @crossWidthRatio;
}

.c {
  left: 0;
}

.d {
  left: @keyWidth;
}

.e {
  left: @keyWidth*2;
}

.f {
  left: @keyWidth*3;
}

.g {
  left: @keyWidth*4;
}

.a {
  left: @keyWidth*5;
}

.b {
  left: @keyWidth*6;
}

.one {
  margin-left: @keyWidth*7;
}

.one.cross {
  margin-left: @keyWidth*7 + @keyWidth - (@keyWidth * @crossWidthRatio/2);
}

.two {
  margin-left: @keyWidth*14;
}

.two.cross {
  margin-left: @keyWidth*14 + @keyWidth - (@keyWidth * @crossWidthRatio/2);
}

.three {
  margin-left: @keyWidth*21;
}

.three.cross {
  margin-left: @keyWidth*21 + @keyWidth - (@keyWidth * @crossWidthRatio/2);
}

.four {
  margin-left: @keyWidth*28;
}

.four.cross {
  margin-left: @keyWidth*28 + @keyWidth - (@keyWidth * @crossWidthRatio/2);
}
View Compiled

var audiolet = new Audiolet();
var lowPass = 200;
var highPass = 300;

var maximumDelayTime = 10.0;
var delayTime = 0.3;

var feedback = 0.5;
var mix = 0.5;

var gate = 1;
var attack = 1;
var decay = .2;
var sustain = .9;
var release = 2;

var sines = [];
var sawes = [];

var lpFilter = new LowPassFilter(audiolet, lowPass);
var hpFilter = new HighPassFilter(audiolet, highPass);
var feedbackDelay = new FeedbackDelay(audiolet, maximumDelayTime, delayTime, feedback, mix);


lpFilter.connect(hpFilter);
hpFilter.connect(feedbackDelay);
feedbackDelay.connect(audiolet.output);

var stoppingKeys = [];

var transposeUp = false;

function makeTone(frequency, stopHandler) {
  sines.push(new Sine(audiolet, frequency));
  //sawes.push(new Saw(audiolet,frequency));

  sines[sines.length - 1].connect(lpFilter);
  //sawes[sawes.length-1].connect(lpFilter);

  //sawes[sawes.length-1].phase = 0;

  stoppingKeys[sines.length - 1] = stopHandler;

  return sines.length - 1;
};

function stopMe(index) {
  sines[index].remove();

  var returnValue = stoppingKeys[index];
  stoppingKeys.splice(index);

  return returnValue;
}

$(".key").bind("mousedown touchstart", function() {

  var className = this.className;
  var tone = className.substring(4, 5);
  var cross = $(this).hasClass("cross");

  var frequency = 0.0;

  $(this).addClass("active");

  var index = $(this).index() - 21;
  frequency = 440 * Math.pow(2, (index / 12));

  var index = makeTone(frequency, $(this));
  $(this).bind("mouseup touchend mouseleave", function() {
    stopMe(index);

    $(this).removeClass("active").unbind("mouseup touchend mouseleave");
  });

});

var keysPressed = [];

$(window).bind("keydown keyup", function(e) {
 
  var up = (e.type == "keyup");
  var key = "";

  if (!up && keysPressed[e.which] == true) {
    e.preventDefault();
    return false;
  } else {

    var zeroClass = (transposeUp ? "one" : "zero");
    var oneClass = (transposeUp ? "two" : "one");
    var twoClass = (transposeUp ? "three" : "two");

    keysPressed[e.which] = !up;
    switch (e.which) {

      case keyCZero: // Y
        key = ".key." + zeroClass + ".c";
        break;
      case 83: // S
        key = ".key." + zeroClass + ".c.cross";
        break;
      case 88: // X
        key = ".key." + zeroClass + ".d";
        break;
      case 68: // D
        key = ".key." + zeroClass + ".d.cross";
        break;
      case 67: // C
        key = ".key." + zeroClass + ".e";
        break;
      case 86: // V
        key = ".key." + zeroClass + ".f";
        break;
      case 71: // G
        key = ".key." + zeroClass + ".f.cross";
        break;
      case 66: // B
        key = ".key." + zeroClass + ".g";
        break;
      case 72: // H
        key = ".key." + zeroClass + ".g.cross";
        break;
      case 78: // N
        key = ".key." + zeroClass + ".a";
        break;
      case 74: // J
        key = ".key." + zeroClass + ".a.cross";
        break;
      case 77: // M
        key = ".key." + zeroClass + ".b";
        break;

      case 188: // , ;
      case keyCOne: // Q
        key = ".key." + oneClass + ".c";
        break;

      case 76: // L 
      case 50: // 2
        key = ".key." + oneClass + ".c.cross";
        break;
      case 190: // . :
      case 87: // W    
        key = ".key." + oneClass + ".d";
        break;
      case 192: // Ö ( GERMAN keyboard )
      case 51: //  3
        key = ".key." + oneClass + ".d.cross";
        break;

      case 189: // -_
      case 69: // E
        key = ".key." + oneClass + ".e";
        break;
      case 82:
        key = ".key." + oneClass + ".f";
        break;
      case 53:
        key = ".key." + oneClass + ".f.cross";
        break;
      case 84:
        key = ".key." + oneClass + ".g";
        break;

      case 54:
        key = ".key." + oneClass + ".g.cross";
        break;
      case keyAOne: // Z 
        key = ".key." + oneClass + ".a";
        break;
      case 55: // 7 
        key = ".key." + oneClass + ".a.cross";
        break;
      case 85: // U 
        key = ".key." + oneClass + ".b";
        break;

      case 73: // I 
        key = ".key." + twoClass + ".c";
        break;
      case 57: // 9 
        key = ".key." + twoClass + ".c.cross";
        break;
      case 79: // O (Ooohhhh)
        key = ".key." + twoClass + ".d";
        break;
      case 48: // 0 (NULL)
        key = ".key." + twoClass + ".d.cross";
        break;
      case 80: // P
        key = ".key." + twoClass + ".e";
        break;
      case 186: // Ü (German keyboard)
        key = ".key." + twoClass + ".f";
        break;

    }

    if (key) {
      if (up) {
        $(key).first().mouseup();
      } else {
        $(key).first().mousedown();
      }

      e.preventDefault();
      return false;
    }
  }

});

$("input[name=lowPass]").bind("change", function() {
  console.log("change detected!");
  
  lowPass = parseInt(this.value);
  lpFilter.frequency.setValue(lowPass);

});
$("input[name=highPass]").bind("change", function() {
  highPass = parseInt(this.value);
  hpFilter.frequency.setValue(highPass);
});

$("input[name=delayTime]").bind("change",function() {
  val = parseFloat(this.value);

  delayTime = val;
  feedbackDelay.delayTime.setValue(delayTime);
});
$("input[name=delayFeedback]").bind("change",function() {
  val = parseFloat(this.value);

  delayFeedback = val;
  feedbackDelay.feedback.setValue(delayFeedback);
});
$("input[name=delayMix]").change(function() {
  val = parseFloat(this.value);

  delayMix = val;
  feedbackDelay.mix.setValue(delayMix);

});

$("input[name=enableDelay]").change(function() {
  if (this.checked) {
    feedbackDelay.feedback.setValue($("input[name=delayFeedback]").val());
    feedbackDelay.mix.setValue($("input[name=delayMix]").val());
    feedbackDelay.delayTime.setValue($("input[name=delayTime]").val());

    $(this).parents("fieldset").find("input").not(this).removeAttr("disabled");
  } else {
    feedbackDelay.feedback.setValue(0);
    feedbackDelay.mix.setValue(0);
    feedbackDelay.delayTime.setValue(0.3);

    $(this).parents("fieldset").find("input").not(this).attr("disabled", "disabled");
  }
});

$("input[name=showKeyboardKeys]").change(function(e) {
  if (this.checked) {
    $("body").removeClass("hideKeyboardKeys");

  } else {
    $("body").addClass("hideKeyboardKeys");
  }
});

var keyCZero = 89; // Y (german)
var keyCOne = 81; // Q (german)
var keyAOne = 90; // Z (german)

$("select[name=keyboardLayout]").change(function() {
  if (this.value == "de") {
    keyCZero = 89;
    keyCOne = 81;
    keyAOne = 90;

    $("#germanY").html("Y");
    $("#germanZ").html("Z");
  } else {
    keyCZero = 90;
    keyAOne = 89;  
    
    if (this.value == "fr") {
      keyCOne = 65; // A
    } else {
    	keyCOne = 81;
    }

    $("#germanY").html("Z");
    $("#germanZ").html("Y");
  }

});


var userLang = navigator.language || navigator.userLanguage; 
if (userLang.indexOf("de")<0) {
  // keys should also be correct at the beginning on an english keyboard
  $("select[name=keyboardLayout]").val("en").trigger("change");
}

$("input[name=transposeUp]").change(function() {
  transposeUp = this.checked;
  $("input[name=showKeyboardKeys]").removeAttr("checked").change();
});


$("input.number").knobby();  
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
  2. https://rawgit.com/oampo/Audiolet/master/src/audiolet/Audiolet.js
  3. https://rawgit.com/grilly86/jquery.knobby.js/master/jquery.knobby.js