├── .gitignore ├── README.md ├── bundle.js ├── css ├── mobile.css └── piano.css ├── dist └── js-css-piano.js ├── garbage.js ├── index.html ├── index.js ├── package.json ├── test.js └── watch.sh /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-css-piano 2 | 3 | Very small module for drawing a piano using js and css 4 | 5 | ## Install: 6 | 7 | npm install --save js-css-piano 8 | 9 | ## Usage 10 | 11 | ~~~js 12 | var p = require('js-css-piano'); 13 | var elemID = 'piano-container'; 14 | 15 | var p = new piano(elemID, { 16 | octaves: 2, 17 | octaveBegin: 3 18 | }); 19 | p.createPiano(); 20 | ~~~ 21 | 22 | Remember to include the [css/piano.css](css/piano.css) 23 | 24 | You can also see [index.html](index.html) which includes the bundle built from [test.js](test.js) 25 | 26 | You can see this example here: 27 | 28 | [https://diversen.github.io/js-css-piano/](https://diversen.github.io/js-css-piano/) 29 | 30 | ## Build and watch example 31 | 32 | ./watch 33 | 34 | ## Inspiration 35 | 36 | [https://codepen.io/zastrow/details/oDBki](https://codepen.io/zastrow/details/oDBki) 37 | 38 | ## License 39 | 40 | MIT © [Dennis Iversen](https://github.com/diversen) -------------------------------------------------------------------------------- /bundle.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o` 24 | for (i = 1; i <= this.options.octaves; i++) { 25 | html += this.getTones(octaveBegin) 26 | octaveBegin++ 27 | } 28 | html += `` 29 | return html 30 | } 31 | 32 | this.getNoteAndOctave = function (note, octave) { 33 | return note + octave 34 | } 35 | 36 | this.getTones = function (octave) { 37 | let octaveHTML = 38 | ` 39 |
  • 40 |
  • 41 |
  • 42 |
  • 43 |
  • 44 |
  • 45 |
  • 46 |
  • 47 |
  • 48 |
  • 49 |
  • 50 |
  • ` 51 | 52 | return octaveHTML 53 | } 54 | } 55 | 56 | module.exports = piano; 57 | 58 | },{}],2:[function(require,module,exports){ 59 | var piano = require('./index'); 60 | 61 | $(document).ready(function () { 62 | 63 | var elemID = 'piano-container'; 64 | var p = new piano(elemID, { 65 | octaves: 1, 66 | octaveBegin: 3 67 | }); 68 | 69 | p.createPiano(); 70 | 71 | 72 | $('#piano-container > .piano li').click(function (e) { 73 | console.log(e.target.dataset) 74 | }) 75 | }) 76 | },{"./index":1}]},{},[2]) 77 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Vzci9saWIvbm9kZV9tb2R1bGVzL3dhdGNoaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJpbmRleC5qcyIsInRlc3QuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsImZ1bmN0aW9uIHBpYW5vKGVsZW1JRCwgb3B0cykge1xuXG4gICAgdGhpcy5lbGVtSUQgPSBlbGVtSURcbiAgICB0aGlzLmVsZW0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChlbGVtSUQpXG4gICAgXG4gICAgdGhpcy5kZWZhdWx0cyA9IHtcbiAgICAgICAgb2N0YXZlczogMixcbiAgICAgICAgb2N0YXZlQmVnaW46IDNcbiAgICB9XG5cbiAgICB0aGlzLm9wdGlvbnMgPSB7fVxuICAgIE9iamVjdC5hc3NpZ24odGhpcy5vcHRpb25zLCB0aGlzLmRlZmF1bHRzLCBvcHRzKVxuICAgIFxuICAgIHRoaXMuY3JlYXRlUGlhbm8gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGxldCBodG1sID0gdGhpcy5nZXRQaWFub0hUTUwoKVxuICAgICAgICB0aGlzLmVsZW0uaW5uZXJIVE1MID0gaHRtbFxuICAgIH1cblxuICAgIHRoaXMuZ2V0UGlhbm9IVE1MID0gZnVuY3Rpb24gKCkge1xuICAgICAgICBcbiAgICAgICAgbGV0IG9jdGF2ZUJlZ2luID0gdGhpcy5vcHRpb25zLm9jdGF2ZUJlZ2luXG4gICAgICAgIGxldCBodG1sID0gYDx1bCBjbGFzcz1cInBpYW5vXCI+YFxuICAgICAgICBmb3IgKGkgPSAxOyBpIDw9IHRoaXMub3B0aW9ucy5vY3RhdmVzOyBpKyspIHsgIFxuICAgICAgICAgICAgaHRtbCArPSB0aGlzLmdldFRvbmVzKG9jdGF2ZUJlZ2luKVxuICAgICAgICAgICAgb2N0YXZlQmVnaW4rKyBcbiAgICAgICAgfVxuICAgICAgICBodG1sICs9IGA8L3VsPmBcbiAgICAgICAgcmV0dXJuIGh0bWxcbiAgICB9XG5cbiAgICB0aGlzLmdldE5vdGVBbmRPY3RhdmUgPSBmdW5jdGlvbiAobm90ZSwgb2N0YXZlKSB7XG4gICAgICAgIHJldHVybiBub3RlICsgb2N0YXZlXG4gICAgfVxuXG4gICAgdGhpcy5nZXRUb25lcyA9IGZ1bmN0aW9uIChvY3RhdmUpIHtcbiAgICAgICAgbGV0IG9jdGF2ZUhUTUwgPSBcbiAgICAgICAgYFxuICAgICAgICAgICAgPGxpIGNsYXNzPVwid2hpdGUgY1wiIGRhdGEtbm90ZT1cIiR7dGhpcy5nZXROb3RlQW5kT2N0YXZlKCdjJywgb2N0YXZlKX1cIj48L2xpPlxuICAgICAgICAgICAgPGxpIGNsYXNzPVwiYmxhY2sgY3NcIiBkYXRhLW5vdGU9XCIke3RoaXMuZ2V0Tm90ZUFuZE9jdGF2ZSgnY3MnLCBvY3RhdmUpfVwiPjwvbGk+XG4gICAgICAgICAgICA8bGkgY2xhc3M9XCJ3aGl0ZSBkXCIgZGF0YS1ub3RlPVwiJHt0aGlzLmdldE5vdGVBbmRPY3RhdmUoJ2QnLCBvY3RhdmUpfVwiPjwvbGk+XG4gICAgICAgICAgICA8bGkgY2xhc3M9XCJibGFjayBkc1wiIGRhdGEtbm90ZT1cIiR7dGhpcy5nZXROb3RlQW5kT2N0YXZlKCdkcycsIG9jdGF2ZSl9XCI+PC9saT5cbiAgICAgICAgICAgIDxsaSBjbGFzcz1cIndoaXRlIGVcIiBkYXRhLW5vdGU9XCIke3RoaXMuZ2V0Tm90ZUFuZE9jdGF2ZSgnZScsIG9jdGF2ZSl9XCI+PC9saT5cbiAgICAgICAgICAgIDxsaSBjbGFzcz1cIndoaXRlIGZcIiBkYXRhLW5vdGU9XCIke3RoaXMuZ2V0Tm90ZUFuZE9jdGF2ZSgnZicsIG9jdGF2ZSl9XCI+PC9saT5cbiAgICAgICAgICAgIDxsaSBjbGFzcz1cImJsYWNrIGZzXCIgZGF0YS1ub3RlPVwiJHt0aGlzLmdldE5vdGVBbmRPY3RhdmUoJ2ZzJywgb2N0YXZlKX1cIj48L2xpPlxuICAgICAgICAgICAgPGxpIGNsYXNzPVwid2hpdGUgZ1wiIGRhdGEtbm90ZT1cIiR7dGhpcy5nZXROb3RlQW5kT2N0YXZlKCdnJywgb2N0YXZlKX1cIj48L2xpPlxuICAgICAgICAgICAgPGxpIGNsYXNzPVwiYmxhY2sgZ3NcIiBkYXRhLW5vdGU9XCIke3RoaXMuZ2V0Tm90ZUFuZE9jdGF2ZSgnZ3MnLCBvY3RhdmUpfVwiPjwvbGk+XG4gICAgICAgICAgICA8bGkgY2xhc3M9XCJ3aGl0ZSBhXCIgZGF0YS1ub3RlPVwiJHt0aGlzLmdldE5vdGVBbmRPY3RhdmUoJ2EnLCBvY3RhdmUpfVwiPjwvbGk+XG4gICAgICAgICAgICA8bGkgY2xhc3M9XCJibGFjayBhc1wiIGRhdGEtbm90ZT1cIiR7dGhpcy5nZXROb3RlQW5kT2N0YXZlKCdhcycsIG9jdGF2ZSl9XCI+PC9saT5cbiAgICAgICAgICAgIDxsaSBjbGFzcz1cIndoaXRlIGJcIiBkYXRhLW5vdGU9XCIke3RoaXMuZ2V0Tm90ZUFuZE9jdGF2ZSgnYicsIG9jdGF2ZSl9XCI+PC9saT5gXG4gICAgICAgICAgICBcbiAgICAgICAgcmV0dXJuIG9jdGF2ZUhUTUxcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gcGlhbm87XG4iLCJ2YXIgcGlhbm8gPSByZXF1aXJlKCcuL2luZGV4Jyk7XG5cbiQoZG9jdW1lbnQpLnJlYWR5KGZ1bmN0aW9uICgpIHtcblxuICAgIHZhciBlbGVtSUQgPSAncGlhbm8tY29udGFpbmVyJztcbiAgICB2YXIgcCA9IG5ldyBwaWFubyhlbGVtSUQsIHtcbiAgICAgICAgb2N0YXZlczogMSxcbiAgICAgICAgb2N0YXZlQmVnaW46IDNcbiAgICB9KTtcbiAgICBcbiAgICBwLmNyZWF0ZVBpYW5vKCk7XG5cblxuICAgICQoJyNwaWFuby1jb250YWluZXIgPiAucGlhbm8gbGknKS5jbGljayhmdW5jdGlvbiAoZSkge1xuICAgICAgICBjb25zb2xlLmxvZyhlLnRhcmdldC5kYXRhc2V0KVxuICAgIH0pXG59KSJdfQ== 78 | -------------------------------------------------------------------------------- /css/mobile.css: -------------------------------------------------------------------------------- 1 | #piano-container { 2 | margin: 0; 3 | position: relative; 4 | } 5 | 6 | .piano>li { 7 | box-sizing: border-box; 8 | margin: 0; 9 | padding: 0; 10 | list-style: none; 11 | position: relative; 12 | float: left; 13 | } 14 | 15 | .piano>.white { 16 | height: 12em; 17 | width: 12%; 18 | z-index: 1; 19 | border: 1px solid #bbb; 20 | background: white; 21 | } 22 | 23 | .piano>.black { 24 | height: 8em; 25 | width: 9%; 26 | margin: 0 0 0 -4.5%; 27 | z-index: 2; 28 | border: 1px solid #000; 29 | background: black; 30 | } 31 | 32 | .piano>.active { 33 | background: grey; 34 | border: 1px solid grey; 35 | } 36 | 37 | .piano>.b, 38 | .piano>.e, 39 | .piano>.g, 40 | .piano>.d, 41 | 42 | .piano>.a { 43 | margin: 0 0 0 -4.5%; 44 | } 45 | -------------------------------------------------------------------------------- /css/piano.css: -------------------------------------------------------------------------------- 1 | #piano-container { 2 | margin: 0; 3 | position: relative; 4 | max-width: 90%; 5 | 6 | } 7 | 8 | .piano>li { 9 | box-sizing: border-box; 10 | margin: 0; 11 | padding: 0; 12 | list-style: none; 13 | position: relative; 14 | float: left; 15 | } 16 | 17 | .piano>.white { 18 | height: 12em; 19 | width: 3em; 20 | z-index: 1; 21 | border: 1px solid #bbb; 22 | background: white; 23 | } 24 | 25 | 26 | .piano>.black { 27 | height: 8em; 28 | width: 2em; 29 | margin: 0 0 0 -1em; 30 | z-index: 2; 31 | border: 1px solid #000; 32 | background: black; 33 | } 34 | 35 | .piano> .active { 36 | border: 1px solid grey; 37 | background: grey; 38 | } 39 | 40 | 41 | .piano>.b, 42 | .piano>.e, 43 | .piano>.g, 44 | .piano>.d, 45 | 46 | .piano>.a { 47 | margin: 0 0 0 -1em; 48 | } 49 | -------------------------------------------------------------------------------- /dist/js-css-piano.js: -------------------------------------------------------------------------------- 1 | /* Package: js-css-piano. Version: 1.0.0. License: MIT. Author: dennis iversen. Homepage: https://github.com/diversen/simple-js-piano#readme */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsCssPiano = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o` 26 | for (i = 1; i <= this.options.octaves; i++) { 27 | let currentOctave = this.options.octaveBegin 28 | html += this.getTones(currentOctave) 29 | this.options.octaveBegin++ 30 | } 31 | html += `` 32 | return html 33 | } 34 | 35 | this.getNoteAndOctave = function (note, octave) { 36 | return note + octave 37 | } 38 | 39 | this.getTones = function (octave) { 40 | let octaveHTML = 41 | ` 42 |
  • 43 |
  • 44 |
  • 45 |
  • 46 |
  • 47 |
  • 48 |
  • 49 |
  • 50 |
  • 51 |
  • 52 |
  • 53 |
  • ` 54 | 55 | return octaveHTML 56 | } 57 | } 58 | 59 | module.exports = piano; 60 | },{"keyboard-tones":2}],2:[function(require,module,exports){ 61 | var keyboardTones = { 62 | a: 'C', 63 | w: 'C#', 64 | s: 'D', 65 | e: 'D#', 66 | d: 'E', 67 | f: 'F', 68 | t: 'F#', 69 | g: 'G', 70 | y: 'G#', 71 | h: 'A', 72 | u: 'A#', 73 | j: 'B' 74 | }; 75 | 76 | module.exports = keyboardTones; 77 | },{}]},{},[1])(1) 78 | }); -------------------------------------------------------------------------------- /garbage.js: -------------------------------------------------------------------------------- 1 | 2 | this.setEvents = function () { 3 | var elems = document.querySelectorAll('#'+ this.elemID + ' li '); 4 | 5 | elems.forEach(function(elem) { 6 | elem.addEventListener("mousedown mouseup", function() { 7 | console.log('clicked') 8 | }); 9 | }); 10 | 11 | /* 12 | $('div').on('mousedown mouseup', function mouseState(e) { 13 | if (e.type == "mousedown") { 14 | //code triggers on hold 15 | console.log("hold"); 16 | } 17 | });*/ 18 | 19 | 20 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 |
    16 | 18 | 19 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function piano(elemID, opts) { 2 | 3 | this.elemID = elemID 4 | this.elem = document.getElementById(elemID) 5 | 6 | this.defaults = { 7 | octaves: 2, 8 | octaveBegin: 3 9 | } 10 | 11 | this.options = {} 12 | Object.assign(this.options, this.defaults, opts) 13 | 14 | this.createPiano = function () { 15 | let html = this.getPianoHTML() 16 | this.elem.innerHTML = html 17 | } 18 | 19 | this.getPianoHTML = function () { 20 | 21 | let octaveBegin = this.options.octaveBegin 22 | let html = `` 28 | return html 29 | } 30 | 31 | this.getNoteAndOctave = function (note, octave) { 32 | return note + octave 33 | } 34 | 35 | this.getTones = function (octave) { 36 | let octaveHTML = 37 | ` 38 |
  • 39 |
  • 40 |
  • 41 |
  • 42 |
  • 43 |
  • 44 |
  • 45 |
  • 46 |
  • 47 |
  • 48 |
  • 49 |
  • ` 50 | 51 | return octaveHTML 52 | } 53 | } 54 | 55 | module.exports = piano; 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-css-piano", 3 | "version": "1.0.7", 4 | "description": "A simple piano keyboard built with js and css", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/diversen/js-css-piano.git" 12 | }, 13 | "keywords": [ 14 | "piano", 15 | "webaudio", 16 | "keyboard" 17 | ], 18 | "author": "dennis iversen", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/diversen/js-css-piano/issues" 22 | }, 23 | "homepage": "https://github.com/diversen/js-css-piano#readme", 24 | "dependencies": {} 25 | } 26 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var piano = require('./index'); 2 | 3 | $(document).ready(function () { 4 | 5 | var elemID = 'piano-container'; 6 | var p = new piano(elemID, { 7 | octaves: 1, 8 | octaveBegin: 3 9 | }); 10 | 11 | p.createPiano(); 12 | 13 | 14 | $('#piano-container > .piano li').click(function (e) { 15 | console.log(e.target.dataset) 16 | }) 17 | }) -------------------------------------------------------------------------------- /watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | watchify test.js -t --debug -o 'bundle.js' 3 | --------------------------------------------------------------------------------