Playing guitar sounds using javaScript
This script simulates guitar sounds dynamically using the Web Audio API, controlled entirely from JavaScript.
To download the script(s), see the script license, and check details like browser compatibility, use the links on the navigation panel at the top of this page.
Try it
This feature only works in modern browsers. JavaScript is needed for this feature.
You can also press keys on your keyboard. Use A,B,C,D,E,F,G for basic chords. Shift+[A,C,D,F,G] plays sharp chords. Press and hold M for minor chords, such as M+G for G minor, and M+Shift+C for C# minor. Press and hold 2 for sus2, 4 for sus4, 6 for sixth, 7 for seventh, 9 for add9, 1 for add11, M and 7 at the same time for minor seventh. However, your keyboard may not allow you to press some combinations (two of mine fail for M+7+C and M+7+Shift+C, for example).
Or try a song which demonstrates layering of multiple instruments, reverb, pitch shifting bends and slides, vibrato and balance:
How this works
The sounds on this page are generated on the fly using the Web Audio API. To create the sound of a guitar string, it does not rely on pre-recorded audio files, and it does not use MIDI or anything else to tell your soundcard to make a guitar sound. Instead, it works like a synthesiser, where a set of frequencies can be specified. This means that it does not rely on you to have a good soundcard, and it always sounds like the same guitar, on any device. As long as the speakers are good enough, anyway. That's the good part. However, it relies on me to create and control every aspect of the sound myself.
Several harmonic frequencies have to be overlaid over each other, each with precise differences in volume, to make the sound of a single guitar string. The volume of each frequency has to be set to rise quickly as the string is plucked, then fade out slowly, like a real guitar string. It allows them to ring as long as they realistically would. It does end up sounding a bit like a synthesiser keyboard, because the sound is a little too perfect; does not contain all of the plectrum scratching sounds, and other wobbles and unexpected overtones of a real guitar string. But it is a synthesiser, so that is not unexpected.
Several guitar string sounds can be overlaid on top of each other with different fundamental frequencies, to create a chord. Little gaps between them can make it sound like a guitar being played arpeggiated (strummed slowly). The higher notes ring for longer, like a real guitar, so a strummed cord has the bass strings ringing for longer than treble strings.
Real guitars are not tuned perfectly, and even with perfect tuning, the strings change tuning slightly as fingers are pressed between frets. So to stop a generated chord from sounding too artificial, the notes are adjusted to be slightly out of tune, which creates a natural chorus effect that sounds more realistic.
The clean and overdrive guitar sounds are made by playing the fundamental frequency at full volume, then the second harmonic frequency at slightly less than half the volume, actually 1/(2^1.3), then the third harmonic frequency at even less of a third of the volume, actually 1/(3^1.3). So basically, each harmonic frequency is played at 1/(n^1.3) of the volume. This ends up sounding a bit like a humbucker, where the higher tones are suppressed more. A single coil might be closer to 1/(n^1.1) for each harmonic frequency. The muted sound is the opposite, with harmonics following a 1/(n^3) formula, and the ringtime dramatically shortened.
Of all of the possible guitar sounds, the "harmonics" are the hardest one to generate. The effect relies on the fundamental frequency and odd harmonics being almost silenced and damped, and having a 1/(n^2.5) formula, but to slowly swell in volume. Even harmonics are stronger, but higher harmonics are reduced more than normal, using a 1/(n^2.2) formula. The even harmonics alternate in volume from higher to lower, which creates a bell chime effect (I do not know if that happens with real guitar strings, but it sounded more realistic).
Overdrive is realistic (unlike a MIDI keyboard). It is generated by boosting the gain, then applying curved clipping above an amplitude threshold, applied to the overall sound wave, so it functions exactly like a real tube amplifier overdrive. It is set to just above the edge-of-breakup level.
Reverb is generated in the same way as a digital reverb pedal, by taking a random amount of the last sounds that were produced over the last few seconds, and replaying them with a natural decay over time.
Browser limitations
All browsers have limits to how many notes they can play at the same time. However, the number is high enough that most songs should be playable.
All browsers have limits to how high a frequency they can play. This is actually dictated by the sample rate of the computer's sound card. On most systems, this sample rate is either 44'100 Hz or 48'000 Hz, and the maximum note the browser can play is half that, so 22'050 Hz or 24'000 Hz, both beyond normal human hearing. On some systems, the sample rate may be as high as 192'000 Hz, so the browser can play notes as high as 96'000 Hz. Firefox's Gecko engine will happily pretend it can cope with higher numbers, but it produces horrible interference sounds back in the normal hearing range, if you request significantly higher frequencies. The Safari WebKit engine and Chromium engine used by most other browsers will clamp high frequencies to half the sample rate, and Chromium will show a warning in the console. Therefore, a frequency of half the sound card's sample rate should be treated as a hard limit, and unexpected sounds could happen if you try playing notes above that. The script forcibly clamps all tones to that limit, and shows a more helpful warning in the console. This clamped frequency would sound wrong if you could hear it, but the vast majority of people cannot hear that high.
In Safari's WebKit (and therefore all browsers on iPhone), the memory usage starts at nearly 120 MB just to render the basic page, and increases the more sounds you play. The script very clearly tells the browser that it has stopped using the parts of the Audio API as they finish, and that the browser can therefore garbage collect and reclaim the memory. However, Safari uses nearly 90 MB more memory every time you play the demo, and it does not release it until you reload the page. Playing it multiple times causes the memory usage to grow and grow without releasing it. Eventually it can get so high that the browser chokes, and struggles to play the sounds, and gets the timings all wrong. This is not a fault with the script, it is a fault with memory leaks in Safari's very poor browser engine.
Firefox's Gecko engine uses nearly 100 MB to render the basic page. It then ramps up the memory a little while it is playing, but it never gets above 50 MB more than it needed just to render the page (usually around 40 MB), and the memory is correctly recovered once the whole audio chain is disconnected at the end. Chromium uses less than 20 MB to render the basic page. While playing the demo, the level climbs in little ramps of less than 1 MB before being garbage collected a few times through the song. In fact, it used a maximum of just 0.7 MB while playing about a third of the demo before garbage collection ran. It barely seems to notice that the script is running, and the memory management is incredibly efficient. There is no contest, Chromium is by far the best here.
Older browsers cannot use the script at all, and at present, it is only supported by the Chromium, Gecko and Webkit engines. Fortunately, these are used by almost all modern browsers. It was also supported in EdgeHTML, which was used by Edge 12-18. However, that engine is not included in any browsers that will run now, so the fact that it works there is irrelevant.
Using the script
This script is not intended to be a major synthesiser, it is not intended for serious production work. It was just an experiment, that ended up good enough to share. It can be made to play simple songs (without drums), but it is quite verbose in the way that things need to be specified. It is not as neat and compact as MIDI might be, and you would need to build your own simple timing system to tell it when to play each note/chord. One example would be an array of notes/chords, with setTimeout or setInterval being used to step through the array one entry at a time.
In theory, it can play as many instruments at the same time as you would like, but in practice, the engine quickly chokes from too many oscillators. You can reduce the number of tones per note so it uses fewer oscillators, to allow more instruments to be played at once, but if you reduce it too far, it sounds nothing like a guitar. So basically, it can cope with a few instruments at once, but you will need to make sure that previous notes are not allowed to ring forever (which would also sound weird). Notes last for between 10 seconds for high notes, and 30 seconds for low notes. You will need to tell the script to either ring for less time, or tell it to stop playing the previous ones before starting a new one.
Browsers will not allow sounds to play until the user clicks on the page, or presses a key, so you should only start playing notes after at least one click or keypress has happened on the page.
Simple use
The most basic use is to play one note. Playing notes is done with the playNotes function. Notes are specified by their frequency (don't worry, there are easy ways to get away from specifying frequencies manually, continue reading). For example, middle C (also known as C4) is 261.625565 hertz.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [261.625565]
};
playNotes(notesParams);
};
</script>
For convenience, the script specifies the reference frequency A4 as a global variable containing the number 440, since 440 Hz is the standard reference frequency used for the A above middle C (known as A4) in almost all modern music, known as A440 or concert pitch. However, it is possible to use any frequency you choose.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [A4]
};
playNotes(notesParams);
};
</script>
With standard intonation (which is always used on modern guitars), also known as equal temperament, is possible to calculate any other note by counting the semitones up or down from A4, and applying that to the formula A4 * 2^( semitonesFromA4 / 12 ). For convenience, the script provides a function getHz to do this for you, so you can just supply a number of semitones up (positive numbers) or down (negative numbers) from A4 (A440 concert pitch):
<script type="text/javascript">
someDiv.onclick = function () {
var frequency = getHz(-9);
var notesParams = {
notes: [frequency]
};
playNotes(notesParams);
};
</script>
Since most guitarists are far more used to working with strings and fret numbers, the script has a convenience object called strings which has properties of all of the common strings: lowE, A, D, G, B, highE and dropD, so the number of the fret can be more simply added to that:
<script type="text/javascript">
someDiv.onclick = function () {
//middle C is the first fret on the B string
var frequency = getHz( strings.B + 1 );
var notesParams = {
notes: [frequency]
};
playNotes(notesParams);
};
</script>
Notes can be specified in an array, containing as many notes as you want.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
getHz(strings.lowE),
getHz( strings.A + 2 ),
getHz( strings.D + 2 ),
getHz( strings.G + 1 ),
getHz(strings.B),
getHz(strings.highE)
]
};
playNotes(notesParams);
};
</script>
If you just want to play a common chord, the script has a convenience object called chords which has properties of A-G, as well as As-Gs for A# to G#, and properties for sus2, sus4, sixth, seventh, add9, add11, minor and minor seventh for all of those base notes:
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: chords.Dsm7
};
playNotes(notesParams);
};
</script>
It is almost always a good idea to stop any existing notes from playing before starting new ones. The playNotes function returns an audioSet object (or null if the browser does not support the required APIs), which can be passed back as an entry in the stopPrevious parameter when calling the function again. The function can also be called with just the stopPrevious parameter, without supplying any notes to play, which will result in it stopping the previous notes without starting any new ones:
<script type="text/javascript">
var audioSet, timeout;
someDiv.onclick = function () {
if( timeout ) {
clearTimeout(timeout);
}
var notesParams = {
notes: [
getHz(strings.lowE),
getHz( strings.A + 2 ),
getHz( strings.D + 2 ),
getHz( strings.G + 1 ),
getHz(strings.B),
getHz(strings.highE)
],
stopPrevious: audioSet ? [audioSet] : null
};
audioSet = playNotes(notesParams);
timeout = setTimeout( function () {
//abruptly stop the notes playing after 3 seconds
playNotes( { stopPrevious: [audioSet] } );
timeout = null;
}, 3000 );
};
</script>
The audioSet object also has the stopPlaying method, which stops just that set of notes from playing, and it also has a playingState property that can be checked at any time to see if it is 'playing', meaning that at least one of its notes is still playing. It gets updated to 'stopping' while it is in the process of reducing the output volume after playing stops, and then 'disconnected' once the oscillators have been stopped and disconnected:
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
getHz(strings.lowE),
getHz( strings.A + 2 ),
getHz( strings.D + 2 ),
getHz( strings.G + 1 ),
getHz(strings.B),
getHz(strings.highE)
]
};
var audioSet = playNotes(notesParams);
setTimeout( function () {
audioSet.stopPlaying();
if( audioSet.playingState == 'playing' ) {
//not yet disconnected, but it will soon
...
}
}, 3000 );
};
</script>
When notes finish playing, the script waits for all oscillators to stop, then disconnects all parts of the Audio API chain relating to that note, so that the JavaScript engine can garbage collect it. It also empties the audioSet object, so that it does not preserve any data, and its stopPlaying method does nothing. This actually takes place some time after the last sound can be heard, because the sound follows an exponential decay which takes a long time to reach 0. It can be as much as 30 seconds, depending on what note was played. If needed, the playNotes function can also be given an onstop callback function, which will be called after all notes have stopped playing, and all cleanup has taken place. This function will be passed a stopEvent object that has two properties, type which will be the string 'stop', and target which will be the audioSet object:
<script type="text/javascript">
var audioSet, timeout;
someDiv.onclick = function () {
var notesParams = {
notes: [
getHz(strings.lowE),
getHz( strings.A + 2 ),
getHz( strings.D + 2 ),
getHz( strings.G + 1 ),
getHz(strings.B),
getHz(strings.highE)
],
onstop: function (stopEvent) {
if( stopEvent.type == 'stop' && stopEvent.target == audioSet ) {
...
}
}
};
audioSet = playNotes(notesParams);
timeout = setTimeout( function () {
//abruptly stop the notes playing after 3 seconds
playNotes( { stopPrevious: [audioSet] } );
timeout = null;
}, 3000 );
};
</script>
When notes are told to stop, they actually take a very short amount of time for the volume to reduce to 0 (50 ms), and then for the oscillators to stop (200 ms). This is done intentionally, because stopping or disconnecting an oscillator immediately will cause it to abruptly slam an oscillating wave from whatever position it was on the wave, to 0, which results in an audible click. So if you are playing a new note instead, for 50 ms the old note will still be faintly audible, and the oscillators continue to use resources for 200 ms. This is actually fairly realistic, as guitar strings never stop instantly, and the old notes can be brifly heard, so the effect sounds quite realistic, and if I did not tell you that it was happening, you probably would not know.
Controlling the sound
By default, a clean guitar sound is used. The sound can be controlled using the sound parameter. The script can be told to use a clean sound, overdrive, harmonics, muted and muted overdrive. Overdrive is generated by applying soft clipping of the wave of all notes that are played at once, unlike most MIDI keyboards, which try to make each string sound overdriven on its own (which sounds terrible when they are played together).
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: chords.E,
sound: 'overdrive'
};
playNotes(notesParams);
};
</script>
By default, each note uses 10 tones (a fundamental and the first 9 harmonics), which gives a reasonable guitar sound. It uses 10 oscillators per note. It is possible to decrease or increase this number. If the number is decreased too far, it will not sound like a guitar. If the number is increased, it can sound quite painful, and not only is there a risk of the browser failing to be able to cope with the number of oscillators, it will also hit the frequency limit for oscillators, which is half the sample rate of the computer's sound card, typically 22'050 or 24'000 Hz. In other words, the frequency of the note you ask it to play, multiplied by the number of tones you ask it to use, must be kept below 22'050 or 24'000 (depending on the computer), or the higher overtones would sound weird, if you can hear them (most people cannot).
With the default 10 tones, the highest note that can be played without harmonic distortion on most computers is fret 32 or 34 on the high E string, but with 20 tones, fret 21 or 23 cannot be used on the high E string. The number of tones is controlled by the numTones parameter. Even the note itself would be too high on fret 73 or 75, but fortunately, most guitars do not go that high.
The first time any note or its overtones gets above the computer's limit, the script will show a message in the debugger console saying what happened. If you want detailed information of every time this happens, including a stacktrace, you can enable the debugNotes parameter. However, that can produce a lot of output, and uses extra processing power, so it should not be used in production code.
By default, the notes will ring for about the same amount of time as a guitar naturally would, which is 30 seconds for low notes and 10 seconds for high notes. This can be lengthened or shortened using the ringtimeFactor parameter, within limits. The factor cannot be less than 0.21, or higher than Number.MAX_SAFE_INTEGER. The volume of the output is kept to a level just below where it would start to distort because it exceeded the maximum and minimum data value. In other words, below where the browser's audio processing would cause a very harsh clipping (which sounds unpleasantly crackly). The volume of the played notes can be controlled using the masterVolume parameter. This, like the master volume on a real amplifier, is applied after any overdrive, so it does not affect the sound when you turn it down, and it still sounds just as overdriven as it normally would.
By default, notes will play equally through both stereo speakers. This can be panned to the left or right by a custom amount using the balance parameter. A value of -1 will play them fully through the left speaker. A value of 0 will play them equally through both speakers. A value of 1 will play them fully through the right speaker. A value of 0.5 will play them more through the right speaker than the left speaker.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: chords.E,
sound: 'overdrive',
numTones: 5,
ringtimeFactor: 3,
balance: 0.3
};
playNotes(notesParams);
};
</script>
A couple of additional parameters are useful when playing multiple notes at once. When playing a chord on a guitar, there is a slight delay between the moment the first string is played, and the second string is played. This delay is often introduced intentionally for effect with arpeggiated playing. The script allows this to be controlled with the delayBetweenNotes parameter, between 0 and Number.MAX_SAFE_INTEGER, with numbers specified in seconds. Negative numbers reverse the ordering of the notes array, so the same chord note array can be used for strumming both downwards and upwards.
When playing a chord, real guitars are not perfect. Their tuning is not perfect, and fingers push different amounts between the frets on different strings. This creates a slight chorus effect, and it is part of what makes a guitar sound like a guitar, rather than an artificial keyboard. This script can simulate that effect with the imperfection parameter, which introduces a random error for each note, up to a maximum of the specified imperfection, in semitones. A value of 0.05 replicates a well-tuned guitar. 0.15 is well within the expected error while playing pieces with complex bends. 0.3 is audibly bad. This can also be used with single notes, but the effect is hardly noticeable there until the value is set higher.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: chords.E,
delayBetweenNotes: 0.04,
imperfection: 0.05,
};
playNotes(notesParams);
};
</script>
Notes that change
In addition to basic notes with one frequency, the script can accept more complex notes. To facilitate that, instead of supplying a basic frequency for each note, a note object can be supplied instead. This can accept several additional properties. The most important of these is the startHz property, which supplies the frequency of the note, just like using only a basic frequency. This will also be used to work out how long the note should ring for. The next property is the volume, which allows the volume of a note to be set individually, differently from the other notes. Importantly, this is applied before any overdrive, so if it is set to a very low value, it will not trigger the overdrive clipping.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz(strings.lowE)
},
{
startHz: getHz( strings.A + 2 ),
volume: 0.5
}
]
};
playNotes(notesParams);
};
</script>
The note object also lets you specify vibrato and pitch changes. These changes happen at specific times that you specify, and the times are always taken as the time after playNotes was called. They ignore the delayBetweenNotes differences, because that makes it far more easy to line up changes that happen to two strings at once, such as two strings that have vibrato starting at the same moment, or a slide or tremolo arm change that takes place on two strings at the same moment.
To generate vibrato, the timedVibrato property accepts an array of timedVibratoValues objects. Each timedVibratoValues object must have a time property, which gives the time in seconds after playNotes was called, that the vibrato must change to the desired new value. It must also have a hz property, which gives the frequency of the vibrato oscillations. It must also have a strength property, which gives the amount that the note changes in each direction, in portions of a semitone (so a value of 1 would be a vibrato that varies the note by a semitone up and a semitone down, which would be an extremely strong vibrato).
Vibrato changes are made at the time you specify, but the strength of the vibrato rapidly ramps up or down to the new value, to avoid clicking sounds with very sudden changes. This is quite similar to a real guitar, where changes in vibrato amplitude cannot be instant. This is why the hz property needs to be specified even when setting the strength to 0, because it needs to know what frequency you want it to vibrato during the transition time. In fact, it never mathematically reaches the strength you ask for. After a vibrato has been told to reduce to 0, the remaining vibrato becomes inaudible within about 0.2 seconds, and the oscillator amplitude effectively reaches 0 (because oscillators cannot output numbers small enough) after 0.8 seconds. For this reason, when setting the strength to 0, it is probably a good idea to set the hz to 0 too:
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz( strings.A + 2 ),
timedVibrato: [
{ time: 1, hz: 4, strength: 0.2 ) },
{ time: 3, hz: 0, strength: 0 ) }
]
}
]
};
playNotes(notesParams);
};
</script>
To allow for notes that change frequency, the timedHz property accepts an array of timedHzValues objects. Each timedHzValues object must have a time property, which gives the time in seconds after playNotes was called, that the change must have reached the desired new value. It must also have a hz property, which gives the new value for the note's frequency. The changes are done in a linear fashion between timed stops. So if startHz specifies a first frequency, and there is just one timedHzValues object that specifies a time of 1 and a hz of 200, the frequency will change from 100 Hz to 200 Hz over the first second, then stay at 200 Hz until the note stops ringing.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz( strings.A + 2 ),
timedHz: [
{ time: 1, hz: getHz( strings.A + 3 ) }
]
}
]
};
playNotes(notesParams);
};
</script>
This can be used to simulate bends, hammers, pull-offs, slides, legato slides, and tremolo arms (which do not actually produce a tremolo effect, since that is a rapid volume pulsation - stupid guitar naming conventions). Normally, the first timedHzValues object in the timedHz array gives the same frequency for its hz property as the startHz had, since it would set the duration that the note remains at that frequency. To simulate a hammer or pull-off, the new frequency would then have to be supplied as a second timedHzValues object, with a very slightly later time. If you set the same time for multiple timedHzValues objects, only the last one will be used. For bends, slides and tremolo arms, the second timedHzValues object would have a significantly later time:
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz( strings.A + 2 ),
timedHz: [
{ time: 1, hz: getHz( strings.A + 2 ) },
{ time: 2, hz: getHz( strings.A + 3 ) }
]
}
]
};
playNotes(notesParams);
};
</script>
Since the startHz is used to calculate the ringing time of the note, if the intention is to replicate a divebomb release (using a tremolo arm) rising to the natural note, then the startHz should be set to the natural note, and the first timedHzValues entry should have its time set to 0, and its hz property set to the divebomb note. A second timedHzValues object should set the natural frequency that you want the note to rise to:
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz( strings.A + 2 ),
timedHz: [
{ time: 0, hz: getHz( strings.A - 10 ) },
{ time: 1, hz: getHz( strings.A + 2 ) }
]
}
]
};
playNotes(notesParams);
};
</script>
When it comes to slides, a simple change in frequency is quite unrealistic, and sounds more like a fretless guitar. Real slides keep bumping into frets, so the frequency jumps in little steps. This will require a lot of timedHzValues objects in the timedHz array, for the start and end of each fret jump. To make this a lot easier, the script provides a convenience function getSlide, which can calculate all the intermediate steps. It needs to know fromTime; the time the slide starts in seconds after playNotes was called, toTime; the time the slide ends in seconds after playNotes was called, fromHz; the frequency at the start of the slide, toHz; the frequency at the end of the slide, and steps; the number of frets (semitones) difference between the start and end of the slide.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz( strings.A + 2 ),
timedHz: getSlide( {
fromHz: getHz( strings.A + 2 ),
toHz: getHz( strings.A + 12 ),
fromTime: 1,
toTime: 2,
steps: 10
} )
}
]
};
playNotes(notesParams);
};
</script>
The getSlide function returns an array. If this needs to be appended to an existing array of timedHzValues objects, use the array concat method to join them.
<script type="text/javascript">
someDiv.onclick = function () {
var notesParams = {
notes: [
{
startHz: getHz( strings.A + 8 ),
timedHz: [
{ time: 0.25, hz: getHz( strings.A + 8 ) },
{ time: 0.5, hz: getHz( strings.A + 10 ) },
{ time: 0.75, hz: getHz( strings.A + 10 ) },
{ time: 1, hz: getHz( strings.A + 8 ) }
].concat( getSlide( {
fromHz: getHz( strings.A + 8 ),
toHz: getHz( strings.A + 11 ),
fromTime: 1.25,
toTime: 1.5,
steps: 3,
} ) )
}
]
};
playNotes(notesParams);
};
</script>
Reverb effect
Reverb is a little different from the other features. It needs to continue a sound after you have told the sound to stop. It needs to act on the combination of notes that are played one after another. It is also quite computationally expensive to produce and use, so you do not want to have a separate reverb configuration for every note that is played. Reverb is therefore a configure-once, use-repeatedly feature. The basic idea is to create a single reverb configuration, and continue to use that same configuration over and over again every time you call playNotes for the same instrument.
Reverb configurations are made using the createReverbConfiguration function, which returns a configuredReverb object. This function requires a few parameters. First is the duration property, which says how long the reverb effect should continue to echo a note after it plays, in seconds. 3 is a good amount to start with. Quieter notes will sound like they fade out sooner, but the echo will actually stop after the specified time. The next property is the exponentialDecayFactor, which says how quickly the echo should start to fade out. This would normally be greater than 1, and 2.5 is a good starting value. 1 will give an unnatural linear fade. Values between 0 and 1 will give an unnatural echo that stops quite abruptly at the end. 0 will give an echo that does not fade, and suddenly stops. Negative numbers are allowed, but sound extremely unnatural, as the reverb effect increases in strength before echoing once very audibly, then stopping. Interesting for "experimental" music, perhaps.
The next property is the reverbVolume, which says how much of the wet (reverb effect) signal to use in the output. The final property is the originalVolume, which says how much of the dry (no effect) signal to use in the output. Neither of these would normally be set to 1, because the addition of the original sound and its echos can add up to a total soundwave large enough to be clipped by the audio processing. Common values are 0.6 for the reverbVolume, and 0.7 for the originalVolume. These properties are both optional, and it will assume a value of 1 for both if they are not specified. A completed reverb configuration is then supplied as the reverb property to playNotes:
<script type="text/javascript">
someDiv.onclick = function () {
var configuredReverb = createReverbConfiguration( {
duration: 3,
exponentialDecayFactor: 2.5,
reverbVolume: 0.6,
originalVolume: 0.7
} );
var notesParams = {
notes: [ getHz( strings.B + 1 ) ],
reverb: configuredReverb
};
var audioSet = playNotes(notesParams);
var notesParams2 = {
stopPrevious = [audioSet];
notes: [ getHz( strings.B + 3 ) ],
reverb: configuredReverb
};
setTimeout( function () {
playNotes(notesParams2);
}, 500 );
};
</script>
If you need to change the configuration, you need to create a new configuration and start using it, and optionally disconnect the old one. It plays echos for a very long time after a note starts. The notes that it is echoing can continue to ring for as much as 30 seconds after they are played, and the echo then lasts for as long as you specify afterwards. Disconnecting while a note is still playing, or while it is still reverberating, will sound terrible. If you want to stop using the reverb configuration and allow the garbage collector to free up the resources, use the onstop parameter to supply a callback function the last time you plan to call playNotes with that configuration, then call the disconnect method of the configuredReverb object. By default, it will wait for the duration to allow any echos to finish playing, then disconnect the reverb configuration. Once disconnected, a configuredReverb object cannot be used to play notes any more, and will act like it was not told to use it:
<script type="text/javascript">
someDiv.onclick = function () {
var configuredReverb = createReverbConfiguration( {
duration: 3,
exponentialDecayFactor: 2.5,
reverbVolume: 0.6,
originalVolume: 0.7
} );
var notesParams = {
notes: [ getHz( strings.B + 1 ) ],
reverb: configuredReverb
};
playNotes(notesParams);
var notesParams2 = {
notes: [ getHz( strings.B + 3 ) ],
reverb: configuredReverb,
onstop: function () {
configuredReverb.disconnect();
}
};
setTimeout( function () {
playNotes(notesParams2);
}, 500 );
};
</script>
If you need it to disconnect immediately, which will abruptly cut off any remaining reverb echos, you can supply the immediate parameter to the disconnect method. Even so, disconnection is not instant, since it will actually take a very short amount of time for the volume to reduce to 0 (50 ms), and then disconnect (200 ms). This is done intentionally to ensure that there are no audible clicks when it is disconnected, where an audio wave would be snapped to 0 from whatever value it was at:
configuredReverb.disconnect(true);
The configuredReverb object has a reverbState property that will be 'ready' while the configuredReverb object is safe to use with playNotes, 'countdown' when it has been told to disconnect and is waiting before disconnection, 'stopping' while it is lowering the output volume before disconnection, and 'disconnected' once it has disconnected. This is not something that needs to be actively monitored, since no futher actions depend on knowing when that disconnection takes place, so there is no callback function to say when it happens. If there is some need to know that it is complete, a simple timeout of 250 ms will be enough.
API documentation
Data types
The script frequently uses the following data type:
- Hz
- Number 0-22050: the frequency of a note, expressed in Hz (cycles per second). This number multiplied by numTones must not be higher than half the sample rate of the computer's sound card, typically 22050 24000. Any higher, and this will cause harmonic distortion due to frequency clamping.
- null, undefined or any unrecognised value: (default) treated as 0.
Global variables
The script adds the following variables to the global scope:
- audioContext
- Object: native AudioContext or webkitAudioContext depending on the browser. This will be created the first time either the playNotes function or the createReverbConfiguration function is called. If you wish to use a custom AudioContext object instead, you can overwrite the global variable with your own object after including this script, but before calling either of those functions. You must not change the variable after one of those functions has been called, or most actions will cause an error to be thrown.
- undefined: initial value before either the playNotes function or the createReverbConfiguration function is called. In browsers that do not support AudioContext or webkitAudioContext, it will remain undefined even after those functions have been called.
- A4
- Number: 440, the frequency of the A4 note. This is used as the reference frequency for the getHz function, and causes it to return values in A440 standard tuning concert pitch. If you change this value, getHz will return frequencies using the new reference frequency, and you will need to call recalculateChords to to update the values stored in the chords variable. (The frequency of the note A4 is used in almost all modern musical standards as the reference frequency for all other notes, and is the reference frequency used when tuning instruments. It was likely chosen because violins, violas and cellos have open A4 strings, and A4 is in the range of flutes, oboes and many other wind instruments.)
- strings
- Object: the strings object.
- chords
- Object: the chords object.
The strings object
The strings global variable is a strings object containing several properties, each of which is the number of semitones lower than the reference note A4 for a common guitar string tuning. This allows getHz to be given fret numbers added to a string, rather than semitone numbers based on the reference note A4. The following string values are available by default:
- dropD
- Integer: -31.
- lowE
- Integer: -29
- A
- Integer: -24
- D
- Integer: -19
- G
- Integer: -14
- B
- Integer: -10
- hghE
- Integer: -5
The chords object
The chords global variable is a chords object containing several properties, each of which is a notes array representing a guitar chord, which can be used with the playNotes function's notesParams parameter. Chords based on each of the standard 12 semitones are provided, in either the standard or most easy complete configuration for the following variants: major, major suspended second, suspended fourth, sixth, seventh, add nine, add eleven, minor, minor seventh. Where possible, these are the open chord. It intentionally does not include all possible variants of a chord, such as power chords, fifths and different voicings, and does not include less common chords. There are simply too many of them, and you would need to create those yourself as needed. However, it includes enough to make most simple songs, and many complex ones too:
- A
- Array: The A major chord as a notes array of Hz values.
- A2
- Array: The Asus2 chord as a notes array of Hz values.
- A4
- Array: The Asus4 chord as a notes array of Hz values.
- A6
- Array: The A6 chord as a notes array of Hz values.
- A7
- Array: The A7 chord as a notes array of Hz values.
- A9
- Array: The Aadd9 chord as a notes array of Hz values.
- A11
- Array: The Aadd11 chord as a notes array of Hz values.
- Am
- Array: The Am chord as a notes array of Hz values.
- Am7
- Array: The Am7 chord as a notes array of Hz values.
- As
- Array: The A# major chord as a notes array of Hz values.
- As2
- Array: The A#sus2 chord as a notes array of Hz values.
- As4
- Array: The A#sus4 chord as a notes array of Hz values.
- As6
- Array: The A#6 chord as a notes array of Hz values.
- As7
- Array: The A#7 chord as a notes array of Hz values.
- As9
- Array: The A#add9 chord as a notes array of Hz values.
- As11
- Array: The A#add11 chord as a notes array of Hz values.
- Asm
- Array: The A#m chord as a notes array of Hz values.
- Asm7
- Array: The A#m7 chord as a notes array of Hz values.
- B
- Array: The B major chord as a notes array of Hz values.
- B2
- Array: The Bsus2 chord as a notes array of Hz values.
- B4
- Array: The Bsus4 chord as a notes array of Hz values.
- B6
- Array: The B6 chord as a notes array of Hz values.
- B7
- Array: The B7 chord as a notes array of Hz values.
- B9
- Array: The Badd9 chord as a notes array of Hz values.
- B11
- Array: The Badd11 chord as a notes array of Hz values.
- Bm
- Array: The Bm chord as a notes array of Hz values.
- Bm7
- Array: The Bm7 chord as a notes array of Hz values.
- C
- Array: The C major chord as a notes array of Hz values.
- C2
- Array: The Csus2 chord as a notes array of Hz values.
- C4
- Array: The Csus4 chord as a notes array of Hz values.
- C6
- Array: The C6 chord as a notes array of Hz values.
- C7
- Array: The C7 chord as a notes array of Hz values.
- C9
- Array: The Cadd9 chord as a notes array of Hz values.
- C11
- Array: The Cadd11 chord as a notes array of Hz values.
- Cm
- Array: The Cm chord as a notes array of Hz values.
- Cm7
- Array: The Cm7 chord as a notes array of Hz values.
- Cs
- Array: The C# major chord as a notes array of Hz values.
- Cs2
- Array: The C#sus2 chord as a notes array of Hz values.
- Cs4
- Array: The C#sus4 chord as a notes array of Hz values.
- Cs6
- Array: The C#6 chord as a notes array of Hz values.
- Cs7
- Array: The C#7 chord as a notes array of Hz values.
- Cs9
- Array: The C#add9 chord as a notes array of Hz values.
- Cs11
- Array: The C#add11 chord as a notes array of Hz values.
- Csm
- Array: The C#m chord as a notes array of Hz values.
- csm7
- Array: The C#m7 chord as a notes array of Hz values.
- D
- Array: The D major chord as a notes array of Hz values.
- D2
- Array: The Dsus2 chord as a notes array of Hz values.
- D4
- Array: The Dsus4 chord as a notes array of Hz values.
- D6
- Array: The D6 chord as a notes array of Hz values.
- D7
- Array: The D7 chord as a notes array of Hz values.
- D9
- Array: The Dadd9 chord as a notes array of Hz values.
- D11
- Array: The Dadd11 chord as a notes array of Hz values.
- Dm
- Array: The Dm chord as a notes array of Hz values.
- Dm7
- Array: The Dm7 chord as a notes array of Hz values.
- Ds
- Array: The D# major chord as a notes array of Hz values.
- Ds2
- Array: The D#sus2 chord as a notes array of Hz values.
- Ds4
- Array: The D#sus4 chord as a notes array of Hz values.
- Ds6
- Array: The D#6 chord as a notes array of Hz values.
- Ds7
- Array: The D#7 chord as a notes array of Hz values.
- Ds9
- Array: The D#add9 chord as a notes array of Hz values.
- Ds11
- Array: The D#add11 chord as a notes array of Hz values.
- Dsm
- Array: The D#m chord as a notes array of Hz values.
- Dsm7
- Array: The D#m7 chord as a notes array of Hz values.
- E
- Array: The E major chord as a notes array of Hz values.
- E2
- Array: The Esus2 chord as a notes array of Hz values.
- E4
- Array: The Esus4 chord as a notes array of Hz values.
- E6
- Array: The E6 chord as a notes array of Hz values.
- E7
- Array: The E7 chord as a notes array of Hz values.
- E9
- Array: The Eadd9 chord as a notes array of Hz values.
- E11
- Array: The Eadd11 chord as a notes array of Hz values.
- Em
- Array: The Em chord as a notes array of Hz values.
- Em7
- Array: The Em7 chord as a notes array of Hz values.
- F
- Array: The F major chord as a notes array of Hz values.
- F2
- Array: The Fsus2 chord as a notes array of Hz values.
- F4
- Array: The Fsus4 chord as a notes array of Hz values.
- F6
- Array: The F6 chord as a notes array of Hz values.
- F7
- Array: The F7 chord as a notes array of Hz values.
- F9
- Array: The Fadd9 chord as a notes array of Hz values.
- F11
- Array: The Fadd11 chord as a notes array of Hz values.
- Fm
- Array: The Fm chord as a notes array of Hz values.
- Fm7
- Array: The Fm7 chord as a notes array of Hz values.
- Fs
- Array: The F# major chord as a notes array of Hz values.
- Fs2
- Array: The F#sus2 chord as a notes array of Hz values.
- Fs4
- Array: The F#sus4 chord as a notes array of Hz values.
- Fs6
- Array: The F#6 chord as a notes array of Hz values.
- Fs7
- Array: The F#7 chord as a notes array of Hz values.
- Fs9
- Array: The F#add9 chord as a notes array of Hz values.
- Fs11
- Array: The F#add11 chord as a notes array of Hz values.
- Fsm
- Array: The F#m chord as a notes array of Hz values.
- Fsm7
- Array: The F#m7 chord as a notes array of Hz values.
- G
- Array: The G major chord as a notes array of Hz values.
- G2
- Array: The Gsus2 chord as a notes array of Hz values.
- G4
- Array: The Gsus4 chord as a notes array of Hz values.
- G6
- Array: The G6 chord as a notes array of Hz values.
- G7
- Array: The G7 chord as a notes array of Hz values.
- G9
- Array: The Gadd9 chord as a notes array of Hz values.
- G11
- Array: The Gadd11 chord as a notes array of Hz values.
- Gm
- Array: The Gm chord as a notes array of Hz values.
- Gm7
- Array: The Gm7 chord as a notes array of Hz values.
- Gs
- Array: The G# major chord as a notes array of Hz values.
- Gs2
- Array: The G#sus2 chord as a notes array of Hz values.
- Gs4
- Array: The G#sus4 chord as a notes array of Hz values.
- Gs6
- Array: The G#6 chord as a notes array of Hz values.
- Gs7
- Array: The G#7 chord as a notes array of Hz values.
- Gs9
- Array: The G#add9 chord as a notes array of Hz values.
- Gs11
- Array: The G#add11 chord as a notes array of Hz values.
- Gsm
- Array: The G#m chord as a notes array of Hz values.
- Gsm7
- Array: The G#m7 chord as a notes array of Hz values.
getHz
getHz(semitonesFromA4)
- semitonesFromA4
- Number: the number of semitones above or below the reference note A4. Positive numbers are for notes higher than A4, negative numbers are for notes lower than A4. Fractional numbers are allowed, such as 0.25 for a note a quarter of a semitone higher than A4. While the script does not force a limit, there is a practical limit to the acceptable range. Beyond 12182.62368343770413048332557, it will return Infinity with the default value of the A4 variable. Beyond 29.369507723654655 it will be higher than the maximum frequency permitted by the Audio API oscillators.
Returns a Hz value for the note based on the current value of the A4 global variable. The return value can be used in the notes array of the playNotes function's notesParams parameter, or the startHz property of a note object, or the hz property of a timedHzValues object, or the hz property of a timedVibratoValues object.
recalculateChords
recalculateChords()
Overwrites the chords global variable with new frequency values based on the current value of the A4 global variable. It is possible to retain a reference to the old object, since when it is replaced, it is replaced with a completely new object.
playNotes
playNotes([notesParams])
- notesParams (optional)
- Object: notesParams object, parameters to say what notes to play, and how to play them.
- null, undefined or any unrecognised value: (default) use default options.
Returns null if the browser does not support the required Web Audio APIs. Can be called with no notesParams to test for null, which indicates that the APIs are not supported.
If the browser supports the required Web Audio APIs, it returns an audioSet object. This will have its playingState property set to 'playing' if notes were played, or 'disconnected' if not.
notesParams object
The notesParams parameter of the playNotes function is specified as an object with the following properties, all of which are optional:
- stopPrevious (optional)
- Array: the audioSet objects to stop playing.
- null, undefined or any unrecognised value: (default) do not stop any audioSet objects from playing.
- notes (optional)
- Array: the set of Hz or note objects to play.
- null, undefined or any unrecognised value: (default) do not play any notes.
- sound (optional)
- String: 'clean' to use a clean guitar sound. 'overdrive' to use an overdriven guitar sound based on a tube overdrive. 'harmonic' to use a clean twelfth-fret harmonic guitar sound. 'muted' to use a clean palm muted guitar sound. 'mutedoverdrive' to use an overdriven palm muted guitar sound.
- null, undefined or any unrecognised value: (default) use a clean guitar sound.
- numTones (optional)
- Integer 1+: the number of harmonic frequencies to use to make the guitar sound. The minimum number will be forced to 1. Higher numbers sound better, but too many can cause it to sound painful, or can cause the browser to run out of resources and choke up. High numbers will limit the note frequencies that can be played. Note frequency multiplied by numTones must be less than half the sample rate of the computer's sound card, typically 22050 24000. Higher frequencies than that will cause a warning message to appear in the browser's debugger console.
- null, undefined or any unrecognised value: (default) use 10 tones.
- rintimeFactor (optional)
- Number 0.21 to Number.MAX_SAFE_INTEGER: The ringtime to use for notes, multiplied by the default. 1 is the same as using the default. Ignored when the sound is 'muted' or 'mutedoverdrive'.
- null, undefined or any unrecognised value: (default) use the default ringtime. When the sound is 'clean', 'overdrive' or 'harmonic', this is 30 seconds for low E and lower notes, and 10 seconds for A4 and higher notes, and a proportional amount in between. When the sound is 'muted' or 'mutedoverdrive', this is 2 seconds.
- masterVolume (optional)
- Number 0+: the relative volume to use, multiplied by the default volume. The minimum number will be forced to 0. The default volume is set to a value that avoids exceeding the audio processing limits. Suggested 0-1 to avoid clipping. Applied after any overdrive, so overdrive continues to sound like overdrive.
- null, undefined or any unrecognised value: (default) use 1.
- balance (optional)
- Number -1 to 1: the left-right balance (panning) of the output sound. -1 is completely left. 0 is centre. 1 is completely right.
- null, undefined or any unrecognised value: (default) sound is centred.
- delayBetweenNotes (optional)
- Number Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER: The delay between playing each of the notes specified in the notes array. Does not have any effect on timedHzValues or timedVibratoValues timings. 0 gives no delay. Negative numbers reverse the playing order of the notes array.
- null, undefined or any unrecognised value: (default) no delay between notes.
- imperfection (optional)
- Number 0+: causes each note to be played randomly out of tune, up to a maximum of the specified amount (randomly above or below the specified frequency). Specified in portions of a semitone, so 1 is a maximum of 1 semitone error. 0.05 replicates a well-tuned guitar, 0.15 is within the acceptable range for some playing.
- null, undefined or any unrecognised value: (default) uses 0.
- reverb (optional)
- Object: configuredReverb object created using createReverbConfiguration.
- null, undefined or any unrecognised value: (default) does not use reverb.
- onstop (optional)
- Function: callback function that will be called when the notes have stopped ringing, all oscillators have stopped, and all parts of the audio chain have been disconnected to allow garbage collection. The function will be passed a stopEvent object.
- null, undefined or any unrecognised value: (default) does not use callback.
- debugnotes (optional)
- Boolean true: show a warning in the browser's debugger console every time a frequency exceeds the limit of the computer's sound card, typically 22050 24000 Hz. Includes a stack trace. Not intended to be used in production code, since this requires extra processing power.
- Boolean false null, undefined or any unrecognised value: (default) only shows a warning in the browser's debugger console the first time a frequency exceeds the limit of the computer's sound card, typically 22050 24000 Hz.
A complete example would look like this:
var notesParams = {
stopPrevious = [ audioSet, audioSet2 ],
notes: [
123,
A4,
getHz( strings.B + 1 ),
{ startHz: getHz( strings.D + 4 ) }
],
sound: 'overdrive',
numTones: 8,
ringtimeFactor: 1.5,
masterVolume: 0.9,
balance: 0.3,
delayBetweenNotes: 0.04,
imperfection: 0.05,
reverb: configuredReverb,
onstop: someFunction,
debugNotes: true
};
note objects
These can be passed to playNotes in the notes array of its notesParams parameter, instead of Hz values. They are a generic object with the following properties, some of which are optional:
- startHz
- Number 0+: frequency of the note in Hz, when it starts. This number will be used to determine the ringtime of the note. If this is the only property provided, then this is is the same as supplying a Hz value directly instead of a note object.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- timedHz (optional)
- Array: the set of timedHzValues objects to use for the note.
- null, undefined or any unrecognised value: (default) do not apply any time related frequency changes, use only the startHz value.
- timedVibrato (optional)
- Array: the set of timedVibratoValues objects to use for the note.
- null, undefined or any unrecognised value: (default) do not apply any vibrato or time related vibrato changes.
- volume (optional)
- Number 0+: the relative volume to use, multiplied by the default volume. The minimum number will be forced to 0. The default volume is set to a value that avoids exceeding the audio processing limits. Suggested 0-1 to avoid clipping. Applied before any overdrive, so if all notes are reduced, it prevents the overdrive from overdriving.
- null, undefined or any unrecognised value: (default) use 1.
timedHzValues objects
These can be used in a note object's timedHz array. They are a generic object with the following properties, all of which are required. If any property is missing, the entire timedVibratoValues object is ignored:
- time
- Number 0+: time in seconds after playNotes was called, that the frequency of the note should reach the specified value. Changes are made in a linear fashion between the note object's startHz and the earliest timedHzValues object, and between each timedHzValues object and the next timedHzValues object. They are not made abruptly at the specified time. If the times are specified in the wrong order, the JavaScript engine will work out the correct order. If two timedHzValues objects have the same time, the second one replaces the first. The timing does not consider the delayBetweenNotes parameter of the notesParams object, to make it easier to synchronise timings for different notes.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- hz
- Number 0+: frequency of the note in Hz, that the note should reach at the specified time. Changes are made in a linear fashion between the note object's startHz and the earliest timedHzValues object, and between each timedHzValues object and the next timedHzValues object. They are not made abruptly at the specified time.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
timedVibratoValues objects
These can be used in a note object's timedVibrato array. They are a generic object with the following properties, all of which are required. If any property is missing, the entire timedVibratoValues object is ignored:
- time
- Number 0+: time in seconds after playNotes was called, that the frequency and amplitude of the vibrato should change to the specified values. Changes are started at the time specified. If the times are specified in the wrong order, the JavaScript engine will work out the correct order. If two timedVibratoValues objects have the same time, the second one replaces the first. The timing does not consider the delayBetweenNotes parameter of the notesParams object, to make it easier to synchronise timings for different notes.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- hz
- Number 0+: frequency of the vibrato oscillations in Hz. Changes are made at the specified time.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- strength
- Number 0+: the amount that the note changes in each direction, in portions of a semitone (so a value of 1 would be a vibrato that varies the note by a semitone up and a semitone down, which would be an extremely strong vibrato). Changes start at the specified time, but do not happen instantly. To avoid audible clicks, changes ramp to the new value quickly, but with a change from a high value to 0, the vibrato may remain audible for around 0.2 seconds. The amplitude of the note changes effectively becomes 0 due to numerical precision limits in JavaScript after about 0.8 seconds.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
getSlide
getSlide(slideParams)
- slideParams
- Object: slideParams objects, parameters to say what what frequency the slide starts and ends at, how many frets that represents, and what the timing should be.
Returns an array of timedHzValues objects that can be used as a timedHz array, to simulate a slide from one fret to another, bumping into the intermediate frets on the way. Returns an empty array if the parameters cannot produce a valid configuration.
slideParams objects
The slideParams parameter of the getSlide function is specified as an object with the following properties, all of which are required:
- fromTime
- Number 0+: time in seconds after playNotes was called, that the slide should start. Must be less than toTime, or getSlide will return an empty array.
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- toTime
- Number Number.MIN_VALUE+: time in seconds after playNotes was called, that the slide should start. Must be greater than fromTime, or getSlide will return an empty array.
- null, undefined or any unrecognised value: (default) not allowed, causes the getSlide function to return an empty array.
- fromHz
- Number 0+: frequency in Hz that the note should be at fromTime
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- toHz
- Number 0+: frequency in Hz that the note should be at toTime
- null, undefined or any unrecognised value: (default) not allowed, forced to be the number 0.
- steps
- Integer 1+: number of frets difference between fromHz and toHz. The frequency will jump quickly in that many steps, evenly spaced between the two frequencies, and evenly spaced in time.
- null, undefined or any unrecognised value: (default) not allowed, causes the getSlide function to return an empty array.
A complete example would look like this:
var slideParams = {
fromHz: getHz( strings.A + 2 ),
toHz: getHz( strings.A + 12 ),
fromTime: 1,
toTime: 2,
steps: 10
};
audioSet objects
Returned by playNotes, if the browser supports the required Web Audio APIs. This will have the following properties and methods:
- playingState
- String: 'playing' while the notes requested in the notes array are playing. 'stopping' if the notes have finished ringing, or if the stopPlaying method has been used, and the volume is being decreased before disconnection of the audio chain. 'disconnected' if there were no notes to play, or after the notes finish ringing, and all parts of the audio chain have been disconnected to allow garbage collection.
- stopPlaying
- Function (method): stopPlaying method. Can be called to prematurely stop the notes from playing.
audioSet.stopPlaying method
audioSet.stopPlaying()
Stops the notes associated with the audioSet from playing, even if they are still ringing. When notes are told to stop, the playingState property of the audioSet object is set to 'stopping', the volume is reduced to 0 over 50 ms, and then the oscillators are stoped and all parts of the audio chain are disconnected after another 150 ms to allow garbage collection, at which point the playingState property is set to 'disconnected'. So it takes a total of 200 ms to disconnect after being told to stop playing. The onstop function will be called once everything has been disconnected.
stopEvent object
Passed to the onstop callback function that is specified in the playNotes function's notesParams parameter. This will happen when the notes have stopped ringing, all oscillators have stopped, and all parts of the audio chain have been disconnected to allow garbage collection. It will have the following properties:
- type
- String: 'stop'.
- target
- Object: the audioSet object that was returned by the playNotes function, which can be used to identify if the correct notes have finished playing.
createReverbConfiguration
createReverbConfiguration(reverbParams)
- reverbParams
- Object: reverbParams object, parameters to say what what how strong the reverb effect should be.
Returns null if the browser does not support the required Web Audio APIs.
If the browser supports the required Web Audio APIs, it returns a configuredReverb object with its reverbState property set to 'ready'.
reverbParams object
The reverbParams parameter of the createReverbConfiguration function is specified as an object with the following properties, some of which are optional:
- duration
- Number Number.MIN_VALUE+: time in seconds that the reverb should last for.
- null, undefined or any unrecognised value: (default) not allowed, forced to be Number.MIN_VALUE.
- exponentialDecayFactor
- Number: how strongly the reverb echo shoyld fade out over the duration. Recommended greater than 1, with 2.5 being a good starting value. 1 will give an unnatural linear fade. Values between 0 and 1 will give an unnatural echo that stops quite abruptly at the end. 0 will give an echo that does not fade, and suddenly stops. Negative numbers cause the reverb to increase in strength before echoing once very audibly, then stopping.
- null, undefined or any unrecognised value: (default) not allowed, forced to be 0.
- reverbVolume (optional)
- Number 0+: says how much of the wet (reverb effect) signal to use in the output. Normally should be less than 1 to avoid clipping when added to the dry signal. 0.6 is a common value.
- null, undefined or any unrecognised value: (default) use 1.
- originalVolume (optional)
- Number 0+: says how much of the dry (no effect) signal to use in the output. Normally should be less than 1 to avoid clipping when added to the wet signal. 0.7 is a common value.
- null, undefined or any unrecognised value: (default) use 1.
A complete example would look like this:
var reverbParams = {
duration: 3,
exponentialDecayFactor: 2.5,
reverbVolume: 0.6,
originalVolume: 0.7
};
configuredReverb objects
Returned by createReverbConfiguration, if the browser supports the required Web Audio APIs. Can be used as the reverb property of the notesParams object passed to playNotes, to allow it to use reverb. Can be used repeatedly without needing to be recreated each time. Only needs to be recreated if the parameters need to be changed. This object will have the following properties and methods:
- reverbState
- String: 'ready' if the configuredReverb object is able to be used. All other values indicate that it should not be used. 'countdown' if the disconnect method has been called without its immediate parameter being set to true, showing that the configuredReverb object will disconnect once the duration has expired. 'stopping' if the disconnect method has been called with its immediate parameter being set to true, or if the countdown has ended, meaning that the volume of the effect is being reduced to 0 prior to disconnecting. 'disconnected' if the configuredReverb object is no longer connected to the audio output, and can no longer be used.
- input
- Object: the input object of configuredReverb object's audio chain, while it is able to accept input. This is not intended for direct use, and is only intended for use by the playNotes function.
- null: when the configuredReverb object has been disconnected.
- disconnect
- Function (method): disconnect method. Can be called to disconnect the configuredReverb object, and allow garbage collection. Should only be called once playNotes has stopped sending audio output to the configuredReverb object, to avoid abrupt cutoff of the audio output.
configuredReverb.disconnect method
configuredReverb.disconnect([immediate])
- immediate (optional)
- Boolean true: start disconnecting the configuredReverb object from the audio chain immediately, without waiting for the reverb configuration's duration. Will cause the reverberation and audio output to abruptly end.
- Boolean false: (defult) wait for the reverb configuration's duration to allow any existing reverberation to end, then start disconnecting the configuredReverb object from the audio chain.
Disconnects the configuredReverb object from the audio chain, and allows garbage collection. Should only be called once playNotes has stopped sending audio output to the configuredReverb object, to avoid abrupt cutoff of the audio output. When it is told to disconnect, it normally waits for the duration of the reverb configuration, just in case it has only just stopped receiving audio input from playNotes, which will still be reverberating. During this time, the configuredReverb object's reverbState will be set to 'countdown'. Once that time expires (or if it is told to disconnect immediately) the reverbState will be set to 'stopping' while the volume of all the reverb components is reduced to 0 over 50 ms. 150 ms after that, all of the reverb components are disconnected, and the reverbState will be set to 'disconnected'.