NexusUI

Getting Started
Changing Colors
Creating a Rack
Syncing Number Boxes

Interfaces

Core

Button
Dial
Number
Position
Slider
Toggle

General

Envelope
Multislider
Piano
RadioButton
Select
Sequencer
TextButton

Mobile

Tilt

Spatialization

Pan
Pan2D

Visualization

Meter
Oscilloscope
Spectrogram

Tuning

Tune

Timing

Interval

Models

Counter
Sequence
Drunk
Matrix

Helpers

ri (random integer)
rf (random float)
clip
scale
mtof
interp
pick
octave
distance
average
coin
 

NexusUI

Web Audio Interfaces

Version 2

NexusUI is a collection of HTML5 interfaces and Javascript helper functions to assist with building web audio instruments in the browser.

In addition to interfaces, the toolkit contains some helpers for tuning and timing. It does not provide any sound-making capabilities –– for that, check out Tone.js, WebPD, Gibber.lib, or the Web Audio API.

This toolkit is designed to help you rapidly prototype your musical ideas. It is lacking some features which would be useful for production –– for example, these interfaces are not responsive (they do not understand percentage widths and heights). NexusUI is an ongoing open-source project; please get in touch on GitHub if you would like to contribute.

Setup

Download NexusUI.js.
(The link grabs the latest version of the file directly from our NPM.)

Or access on a CDN if you want a hosted file:

  • Development
  • Production

Link to NexusUI.js in your HTML file.


<html>
  <head>
    <script src="path/to/NexusUI.js"></script>
  </head>
  <body>

  </body>
</html>
       
Creating an Interface

You can transform regular HTML elements into NexusUI interfaces (<div> is recommended). By default, the interface component will adopt the height and width of the target element as specified in your CSS or in the element's style. Alternatively, you can specify the interface component's size in JavaScript when you create it (see the API for each interface).


<html>
  <head>
    <script src="path/to/NexusUI.js"></script>
  </head>
  <body>

    <div id="dial"></div>

    <script>
      var dial = new Nexus.Dial('#dial')
    </script>

  </body>
</html>
       

You can also transform many elements at once by creating a rack.

Dynamically Adding and Removing Interfaces

You can also dynamically add interfaces to your page at any point using Nexus.Add. This lets you add many interfaces to the same parent element. You can later remove the interface using the interface's .destroy() method.


<html>
 <head>
   <script src="path/to/NexusUI.js"></script>
 </head>
 <body>

   <div id="instrument"></div>

   <script>
     var dial = Nexus.Add.Dial('#instrument',{
       'size': [100,100]
     });

     var slider = Nexus.Add.Slider('#instrument',{
       'size': [25,100]
     });

     // then, to remove them tlater
     dial.destroy();
     slider.destroy();
   </script>

 </body>
</html>
      
Listening for Interaction Events

Every NexusUI interface fires an event function when the interface is interacted with or changed programatically through the interface's API. If you use Node.JS, this syntax should look familiar –– every NexusUI interface extends Node.js's built-in EventEmitter.


var dial = new Nexus.Dial("#dial");

dial.on('change',function(v) {
  // v holds the new numeric value of the dial
});
       

Many of the core interfaces also fire events on mouse click and mouse release. The following interfaces accept click and release events: button, dial, number, pan, pan2d, position, slider, textbutton, toggle. We hope to develop this feature for all interfaces in the future.


 var slider = new Nexus.Slider("#slider");

 slider.on('click',function() {
   console.log('clicked!');
 });

 slider.on('release',function() {
   console.log('released');
 });
        
Interface Options

Each interface has an API through which you can customize its values and interaction modes. Common options include:

  • Setting a numeric range for the value of the interface
  • Absolute or relative mouse interaction
  • Vertical, horizontal, or radial (for dials) interaction & orientation

Example:

See the Pen MT - Interface Options Demo by Ben Taylor (@taylorbf) on CodePen.

Documentation

Check out the API for more details on interface options, as well as for tutorials on tuning and timing mechanisms.




 

Colors

You can change an interface's colors using .colorize()

See the Pen Colors by Ben Taylor (@taylorbf) on CodePen.



Alternately, you can set Nexus.colors, which will be inherited by every interface created from that point on. This is useful if you want all of your interface components to share the same color scheme. See example on CodePen.

A complete list of color properties used in the interfaces are: accent, fill, dark, light, mediumDark, mediumLight. Not all interfaces use all of these colors.
 

Creating a Rack

You can create many interface components at once using Nexus.Rack(). Here is an example:

See the Pen Creating a Rack by Ben Taylor (@taylorbf) on CodePen.



First, create an element that will be the container of the rack.

Inside that container, create several elements with nexus-ui attributes describing what type of interface it should transform into, such as nexus-ui="dial".

Give each interface element an ID which will be used to identify it in JavaScript later.

In your JavaScript, use new Nexus.Rack("#containerID") to transform the container element into a rack. When this happens, any elements inside the container with valid nexus-ui attributes will be transformed into interface elements.
Using a Custom Attribute

Nexus.Rack optionally lets you specify which attribute to look for ('nexus-ui' by default). This is useful for using NexusUI with React, which requires data- attribute prefixes.

HTML:


  <div id="target">
    <div data-nx="dial"></div>
  </div>
                

JS:


  var synth = new Nexus.Rack("#target",{
    attribute: 'data-nx'
  })
                


Rack Methods
.hide()
.show()
.colorize() (See changing colors)


 

Linking a Number Box to a Dial or Slider

A number box can be linked to the value of a dial or slider using .link()

In this case, when the dial or slider is updated, the number box will show its numeric value. When the number box is updated or interacted with, the dial or slider will be updated to the new value as well.

See the Pen Number Box Sync by Ben Taylor (@taylorbf) on CodePen.

Tuning

Nexus.note() and Nexus.tune offer ways to manage pitches and scales.


// Nexus.note(0) returns the frequency of the 1st note of the scale
synth.frequency.value = Nexus.note(0);

// Nexus.note(2) returns the frequency of the 3rd note of the scale
synth.frequency.value = Nexus.note(2);
     

By default the scale is an equal-tempered major scale. An API for changing the scale is below.

This approach emphasizes algorithmic composition by using numbers to traverse musical scales.

Turning Scale Degrees to Frequency Values

Nexus.note() provides a method for turning scale degrees into frequency values which your web audio synth will understand.

       
// Nexus.note(0) returns the frequency of 1st note of the scale
synth.frequency.value = Nexus.note(0);

// Nexus.note(1) returns the frequency of 2nd note of the scale
synth.frequency.value = Nexus.note(1);

// Negative numbers wrap to a lower octave
// Nexus.note(-1) returns the frequency of the top note of the scale, 1 octave lower
synth.frequency.value = Nexus.note(-1);

// You can also specify the octave using an optional second parameter
// Nexus.note(0,2) returns the frequency of the 1st note of the scale, 2 octaves up.
synth.frequency.value = Nexus.note(0,2);

// Nexus.note(0,-1) returns the frequency of the 1st note of the scale, 1 octave lower
synth.frequency.value = Nexus.note(0,-1);
    

By using counters or number generators in tandem with Nexus.note(), you can play scales, arpeggios, etc.

       
 // Play 5 octaves of the major scale

 var counter = new Nexus.Counter( 0, 36 );

 var beat = new Nexus.Interval(200,function() {
   synth.frequency.value = Nexus.note( counter.next() );
 })
       
     

Getting Pitch Ratios or adjusted MIDI values

If working with an audio sample, you may wish to tune your sample by changing its playback speed. You can use Nexus.tune.ratio() to get the frequency ratio of a note to the root of your scale. This ratio can be used to set the playback speed of your sample.

        
// This gives you the pitch ratio between the 5th note in scale and the root.
Nexus.tune.ratio(4)  // returns 1.49830693925

// You can use this to set the playback speed of a sampler
sampler.playbackRate = Nexus.tune.ratio(4)
        
      

Changing the Scale

By default, the scale is an equal-tempered major scale.

We do not provide any other built-in scales. However, we provide two ways for you to create your own scale.

Describing a Scale

To define your own scale, you can call .createScale() with a list of the chromatic scale degrees you want in your scale.

// Create a minor scale
Nexus.tune.createScale(0,2,3,5,7,8,10);

// Now, Nexus.note() will refer to the scale you defined
Nexus.note(0) // returns the frequency number of the root
Nexus.note(1) // returns the frequency number of a Major 2nd
Nexus.note(2) // returns the frequency number of a Minor 3rd
    

Describing a Just Intonation Scale

You can also define just intonation scales. To do so, call .createJIScale() with your own list of pitch ratios.

// Create a major scale in just intonation
Nexus.tune.createJIScale( 1/1, 9/8, 5/4, 4/3, 3/2, 8/5, 5/3, 15/8 );


// Now, Nexus.note() will refer to the scale you defined
Nexus.note(0) // returns the frequency number for ratio 1/1
Nexus.note(1) // returns the frequency number for ratio 9/8
Nexus.note(2) // returns the frequency number for ratio 5/4
    

Changing the root

By default, the root of the scale is Middle C. Therefore, Nexus.note(0) returns the frequency for Middle C.

You can set the root using Nexus.tune.root, which is a frequency value.


// Set the root to 3 octaves below Middle C.
Nexus.tune.root = Nexus.note(0,-3);
    
In this way, you can change your scale root on the fly by setting it to other notes of the scale you are currently using.

The root can also be set using traditional musical values like "C3" or "A1". This is the only place in this toolkit where this type of music notation is used.


// Set the root to low A.
Nexus.tune.root = "A1";
    

You can also set Nexus.tune.root direction to a frequency value, for example if you are working in Just Intonation.


// Set the root of your scale to 120hz.
Nexus.tune.root = 120;
    

Timing

Nexus.Interval creates a repeating pulse using WAAClock.


// Create a pulse of 100ms. At each pulse, the callback function is executed.

var interval = new Nexus.Interval(100, function() {
  console.log('beep');
})
       

This interval uses a web audio context to handle timing, so it is more accurate than a regular setInterval or setTimeout

Autostart

Be default, the interval will automatically start when it is created. However a third parameter can set autostart to false.


// Create a pulse that does not autostart.

var interval = new Nexus.Interval(100, function() {
  console.log('beep');
}, false)
      

Changing the Interval Period


// Change your interval period to 200ms.
interval.ms(200)
      

All methods


// Create a pulse of 1 second
var interval = new Nexus.Interval(1000, function() {
  console.log('beep');
})

// Stop the pulse
interval.stop();

// Start the pulse
interval.start();

// Change the interval time
interval.ms(200);

// Change the function that is called at each pulse
interval.event = function() {
  console.log("bloop");
}
      

Matrix Model

The Matrix Model lets you read, write, and manipulate a two-dimensional array of data (such as the data that is visualized by a sequencer).


Creating a Matrix Model from scratch...

matrix = new Nexus.Matrix(5,10);
       




Accessing a Matrix's Values


matrix.pattern contains a 2-dimensional JavaScript array. matrix.pattern[0] contains an array of the first row's values. matrix.pattern[0][0] contains the value of the first cell in the first row.


var matrix = new Nexus.Matrix(5,10);
console.log( matrix.pattern );




Toggling cells


matrix.toggle flips a cell or group of cells to their opposite state


var matrix = new Nexus.Matrix(5,10);
matrix.toggle.cell(2,2)






matrix.toggle.row(2)






matrix.toggle.column(2)






matrix.toggle.all()





Setting cells


matrix.toggle flips a cell or group of cells to their opposite state


var matrix = new Nexus.Matrix(5,10);
matrix.set.cell(0,0,1)






matrix.set.row(0,[0,1,0,1,0,1,0,1,0,1])






matrix.set.column(0,[0,1,1,1,0])






matrix.set.all([
  [0,1,0,1,0,1,0,1,0,1],
  [0,1,0,1,0,1,0,1,0,1],
  [0,1,0,1,0,1,0,1,0,1],
  [0,1,0,1,0,1,0,1,0,1],
  [0,1,0,1,0,1,0,1,0,1]
])





Rotating


matrix.rotate moves parts of the matrix to the right or left


var matrix = new Nexus.Matrix(5,10);
matrix.toggle.cell(0,0)






matrix.rotate.row(0)






matrix.rotate.row(0,4)






matrix.rotate.column(5)






matrix.rotate.all(2)





Populating with Patterns


matrix.populate can set row or column using a pattern.


var matrix = new Nexus.Matrix(5,10);
matrix.populate.row( 0, [0,1] )






var matrix = new Nexus.Matrix(5,10);
matrix.populate.column( 0, [0,1] )






var matrix = new Nexus.Matrix(5,10);
matrix.populate.all( [0,1] )





Populating with Probability


matrix.populate can set row or column using a pattern of probability values.


var matrix = new Nexus.Matrix(5,10);
matrix.populate.row( 0, [0.3, 0.6] )






var matrix = new Nexus.Matrix(5,10);
matrix.populate.column( 0, [0.5] )






var matrix = new Nexus.Matrix(5,10);
matrix.populate.all( [0.2, 0.5, 0.2, 0.8] )





Matrix and the Sequencer Interface

The sequencer interface uses a matrix model to hold its grid of values. You can use any of the above matrix manipulation methods on your sequencer grid, using the sequencer's .matrix property.

  var sequencer = new Nexus.Sequencer("#target",5,10);
  sequencer.matrix.toggle.row(2);
  sequencer.matrix.set.row(1, [0,1,0,1,0,1,0,1,0,1])
      




Counter Model

A Counter model is an object with a minimal API for counting up or down.


counter = new Nexus.Counter(0,10)
counter.next()    // returns 0
counter.next()    // returns 1
counter.next()    // returns 2
// ...
counter.next()    // returns 9
counter.next()    // returns 10
counter.next()    // returns 0
         

Modes

The counter method takes an optional third argument for the counting mode, which can be up, down, drunk (randomly chooses to step up or down), or random (chooses a random number from within the counting range).

counter = new Nexus.Counter(0,10,"down")
counter.next()    // returns 10
counter.next()    // returns 9
counter.next()    // returns 8
        

Properties


counter.min = 10
counter.max = 100
counter.mode = "down"
counter.value = 50  // jumps the counter to this value
        

Sequence Model

A Sequence model steps through a series of numeric values defined by you.


var sequence = new Nexus.Sequence([1,3,5,7])
sequence.next()    // returns 1
sequence.next()    // returns 3
sequence.next()    // returns 5
sequence.next()    // returns 7
sequence.next()    // returns 1
sequence.next()    // returns 3
         

Modes

The sequence model takes an optional second argument for the sequence mode, which can be up, down, drunk (randomly chooses to step up or down), or random (chooses a random number from within the counting range).

var sequence = new Nexus.Sequence([1,3,5,7],"down")
sequence.next()    // returns 7
sequence.next()    // returns 5
sequence.next()    // returns 3
sequence.next()    // returns 1
sequence.next()    // returns 7
sequence.next()    // returns 5
        

Properties


sequence.values = [2,4,5,8]   // updates the sequence of values
sequence.mode = "drunk"
sequence.value = 2   // this jumps to where the '2' value is in the sequence, which is index 0.
        

Drunk Model

A Drunk Model does a "drunk walk" through a given range, meaning it will randomly step up or down by a given increment.


// This drunk model has a range of 0 to 100
// Its starting value is 50
// Its stepping increment is 10
var drunk = new Nexus.Drunk(0,100,50,10)
drunk.next()    // returns 40
drunk.next()    // returns 50
drunk.next()    // returns 40
drunk.next()    // returns 30
drunk.next()    // returns 40
drunk.next()    // returns 30
         

Properties


drunk.min = 100
drunk.max = 1000
drunk.increment = 100
drunk.value = 500   // jumps to this value