Musical Scales

In this example below you will see how to do a Musical Scales with some HTML / CSS and Javascript

Thumbnail
This awesome code was written by AstroDroid, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright AstroDroid ©

Technologies

  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>Musical Scales</title>
  
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">

  
      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>

  <h1>Musical Scales</h1>
<p>Check the console for more info</p>
  
  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/AstroDroid/musical-scales-MbOLMZ */
body {
  padding: 0 50px;
}


/*Downloaded from https://www.codeseek.co/AstroDroid/musical-scales-MbOLMZ */
/*
  TODO: fix Scale.prototype.toString
*/
/*
 *  # #  # # #
 * C D EF G A B
 * 012345678901
 */
var Accidental = /** @class */ (function () {
    function Accidental(symbol, steps) {
        this._symbol = symbol;
        this._steps = steps;
    }
    Object.defineProperty(Accidental.prototype, "symbol", {
        get: function () { return this._symbol; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Accidental.prototype, "steps", {
        get: function () { return this._steps; },
        enumerable: true,
        configurable: true
    });
    Accidental.DOUBLEFLAT = new Accidental('♭♭', -2); // 𝄫
    Accidental.FLAT = new Accidental('♭', -1);
    Accidental.NATURAL = new Accidental('♮', 0);
    Accidental.SHARP = new Accidental('♯', 1);
    Accidental.DOUBLESHARP = new Accidental('x', 2); // 𝄪
    return Accidental;
}());
var Note = /** @class */ (function () {
    function Note(letter, steps, accidental) {
        if (accidental === void 0) { accidental = Accidental.NATURAL; }
        this._letter = letter;
        this._steps = steps;
        this._accidental = accidental;
        if (this.accidental.steps === 0)
            Note._naturalNotes[steps] = this;
    }
    Object.defineProperty(Note.prototype, "letter", {
        get: function () { return this._letter; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Note.prototype, "steps", {
        get: function () { return this._steps; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Note.prototype, "accidental", {
        get: function () { return this._accidental; },
        enumerable: true,
        configurable: true
    });
    Note._naturalNotes = {};
    Note.C = new Note('C', 0);
    Note.Cs = new Note('C', 1, Accidental.SHARP);
    Note.Db = new Note('D', 1, Accidental.FLAT);
    Note.D = new Note('D', 2);
    Note.Ds = new Note('D', 3, Accidental.SHARP);
    Note.Eb = new Note('E', 3, Accidental.FLAT);
    Note.E = new Note('E', 4);
    Note.F = new Note('F', 5);
    Note.Fs = new Note('F', 6, Accidental.SHARP);
    Note.Gb = new Note('G', 6, Accidental.FLAT);
    Note.G = new Note('G', 7);
    Note.Gs = new Note('G', 8, Accidental.SHARP);
    Note.Ab = new Note('A', 8, Accidental.FLAT);
    Note.A = new Note('A', 9);
    Note.As = new Note('A', 10, Accidental.SHARP);
    Note.Bb = new Note('B', 10, Accidental.FLAT);
    Note.B = new Note('B', 11);
    return Note;
}());
var Scale = /** @class */ (function () {
    function Scale(arg, range) {
        if (range === void 0) { range = 12; }
        if (typeof arg === 'number')
            this._setDecimal(arg, range);
        else if (typeof arg === 'string')
            this._setBinary(arg);
        else if (typeof arg === 'Scale')
            this._setNotes(arg._notes, arg._range);
        else
            this._setNotes(arg, range);
    }
    Object.defineProperty(Scale, "major", {
        // Diatonic
        get: function () { return new Scale('101011010101'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "minor", {
        get: function () { return Scale.major.getMode(5); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "ionian", {
        get: function () { return Scale.major; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "dorian", {
        get: function () { return Scale.major.getMode(1); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "phrygian", {
        get: function () { return Scale.major.getMode(2); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "lydian", {
        get: function () { return Scale.major.getMode(3); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "mixolydian", {
        get: function () { return Scale.major.getMode(4); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "aeolian", {
        get: function () { return Scale.minor; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "locrian", {
        get: function () { return Scale.major.getMode(6); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "melodicMinor", {
        // Synthetic
        get: function () { return new Scale('101101010101'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "harmonicMinor", {
        get: function () { return new Scale('101011011010'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "harmonicMajor", {
        get: function () { return new Scale('101011011001'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "wholeTone", {
        get: function () { return new Scale('101010101010'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "chromatic", {
        get: function () { return new Scale('111111111111'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "pentatonicMinor", {
        get: function () { return new Scale('100101010010'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "pentatonicMajor", {
        get: function () { return Scale.pentatonicMinor.getMode(1); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "blues", {
        get: function () { return new Scale('100101110010'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "bluesMajor", {
        get: function () { return Scale.blues.getMode(1); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "octatonicMinor", {
        get: function () { return new Scale('110110110110'); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale, "octatonicMajor", {
        get: function () { return Scale.octatonicMinor.getMode(1); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale.prototype, "notes", {
        get: function () { return this._notes.slice(0); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale.prototype, "range", {
        get: function () { return this._range; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale.prototype, "length", {
        get: function () { return this._notes.length; },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale.prototype, "intervals", {
        get: function () {
            var _this = this;
            return this._notes.map(function (note, i) { return (_this._notes[i + 1] || _this._range) - note; });
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale.prototype, "binary", {
        get: function () {
            var _this = this;
            return Array.from(new Array(this._range)).map(function (a, i) { return _this._notes.indexOf(i) < 0 ? '0' : '1'; }).join('');
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Scale.prototype, "decimal", {
        get: function () {
            return parseInt(this.binary.slice(1).split('').reverse().join(''), 2);
        },
        enumerable: true,
        configurable: true
    });
    Scale.prototype.modifyRange = function (range) {
        range = range < 0 ? 0 : Math.floor(range);
        for (var i = 0; i < this._notes.length; i++) {
            if (this._notes[i] >= range) {
                var notes = this._notes.slice(0, i);
                return new Scale(notes, range);
            }
        }
        return new Scale(this._notes, range);
    };
    Scale.fromIntervals = function (intervals) {
        var notes = [0];
        intervals.forEach(function (interval, i) { return notes.push(notes[i] + interval); });
        var range = notes.pop();
        return new Scale(notes, range);
    };
    Scale.prototype._setNotes = function (notes, range) {
        if (range === void 0) { range = this._range; }
        notes = Array.from(notes).map(function (a, i) { return (a - notes[0]) % range; });
        this._notes = notes;
        this._range = range;
    };
    Scale.prototype._setBinary = function (binary) {
        var notes = [];
        binary.split('').forEach(function (char, i) { return char === '1' && notes.push(i); });
        this._setNotes(notes, binary.length);
    };
    Scale.prototype._setDecimal = function (decimal, range) {
        if (range === void 0) { range = this._range; }
        this._setBinary(('1' + decimal.toString(2).split('').reverse().join('') + '0'.repeat(range)).slice(0, range));
    };
    Scale.prototype.getMode = function (degree) {
        var _this = this;
        if (degree === void 0) { degree = 0; }
        degree = degree < 0 ? 0 : Math.floor(degree) % this.length;
        return new Scale(this._notes.slice(degree).concat(this._notes.slice(0, degree).map(function (note) { return note + _this._range; })));
    };
    Scale.prototype.getModes = function () {
        var modes = [];
        for (var i = 0; i < this.length; i++) {
            modes.push(this.getMode(i));
        }
        return modes;
    };
    Scale.prototype.isValid = function () {
        for (var i = 0; i < this.length; i++)
            if (this._notes[i] >= this._notes[i + 1])
                return false;
        return (new Set(this._notes)).size === this.length;
    };
    Scale.prototype.modifyNote = function (noteIndex, steps) {
        var notes = this.notes;
        notes[noteIndex] += steps;
        return new Scale(notes);
    };
    /*getSpelling(startingNote: Note = Note.C, accidentalType: Accidental = Accidental.FLAT) {
      return this._notes.map(note => Note.getNote((startingNote.steps + note) % this._range, accidentalType));
    }*/
    Scale.prototype.getScaleDegrees = function (base) {
        if (base === void 0) { base = Scale.major; }
        return this._notes.map(function (a, i) { return Scale.getAccidental(a - base._notes[i]) + (i + 1); });
    };
    Scale.prototype.toString = function () {
        //return this.getSpelling().join(' ');
        return this.getScaleDegrees().join(' ');
    };
    Scale.prototype.equals = function (scale) {
        if (this === scale)
            return true;
        if (this.length !== scale.length || this._range !== scale._range)
            return false;
        for (var i = 0; i < this.length; i++)
            if (this._notes[i] !== scale._notes[i])
                return false;
        return true;
    };
    Scale.getAll = function (range) {
        if (range === void 0) { range = 12; }
        var scales = [];
        var max = Math.pow(2, range - 1);
        for (var i = 0; i < max; i++) {
            scales.push(new Scale(i));
        }
        return scales;
    };
    Scale.getAccidental = function (steps) {
        if (steps === 0)
            return '';
        if (steps > 0)
            return (steps % 2 ? '♯' : '') + 'x'.repeat(Math.floor(steps / 2));
        steps = Math.abs(steps);
        return (steps % 2 ? '♭' : '') + '♭♭'.repeat(Math.floor(steps / 2));
        // return ['♭♭', '♭', '', '♯', 'x'][offset + 2]; // '𝄫', '♭', '', '♯', '𝄪'
    };
    return Scale;
}());
(function () {
    //console.log(Scale.major);
    console.log('major,minor,ionian,dorian,phrygian,lydian,mixolydian,aeolian,locrian,melodicMinor,harmonicMinor,harmonicMajor,wholeTone,chromatic,pentatonicMajor,pentatonicMinor,blues,bluesMajor,octatonicMinor,octatonicMajor'.split(',').map(function (a) { return a + '\t' + Scale[a]; }));
    // console.log(Scale.getAll().filter(a => Math.max(...a.intervals) < 3)); //.map(a => a.notes.join(' ')));
    // console.log(getModified());
    // console.log([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5].map(a => Scale.getAccidental(a)));
    // All unique modified scales
    function getUniqueModified(scale) {
        if (scale === void 0) { scale = Scale.major; }
        return Array.from(new Set(// Remove dupes
        scale.getModes().map(function (mode) {
            return modifyScale(mode)
                .filter(function (a) { return a.isValid(); })
                .map(function (a) { return a + '\t\t\t' + a.notes.join(' '); });
        }).reduce(function (a, b) { return a.concat(b); })));
    }
    function getModified(scale, steps) {
        var scales = [];
        for (var i = 0; i < scale.length; i++) {
            scales.push(scale.modifyNote(i, steps));
        }
        return scales;
    }
    function modifyScale(scale) {
        return [scale].concat(getModified(scale, -1), getModified(scale, 1));
    }
})();

Comments