Home

Awesome

Overview

Facet is an open-source live coding system for algorithmic music and synthesis. With a code editor in the browser and a pair of NodeJS servers running locally on your machine, Facet can generate and sequence audio, MIDI, OSC, and image data.

Facet runs on MacOS, Linux, and Windows.


Installation and getting started

  1. Download and install Node.js (must be v14 or greater) and npm: https://www.npmjs.com/get-np
  2. Download or clone the Facet repository.
  3. In a terminal, navigate to the root of the Facet repository, and run npm install.
  4. After the previous command completes, run npm run facet. This will start the servers that run in the background for generating and patterns and keeping time. If running on Windows: Windows has a firewall by default for local connections (on the same private network), and it needs to be disabled, or you can manually allow the connection via the confirmation dialog from the Windows firewall system when starting up the servers.
  5. In a browser tab (Firefox, Chrome, and Edge work best), navigate to http://localhost:1124. This is the browser-based code editor which can also handle stereo audio playback.
  6. Copy this command into the code editor in the browser: $('test').sine(100).play(); Move your cursor so it's on the line. Hit [ctrl + enter] to run the command. The code editor application will always briefly highlights to illustrate what command(s) ran. You should hear a sine wave playing through your browser tab. Hit [ctrl + .] or [ctrl + /] (Windows) to stop.

Facet commandyntax

Facet commands are based entirely around JavaScript, using a class called a FacetPattern. In order to produce audio or MIDI output, create an instance of a FacetPattern, and run some methods:

new FacetPattern('example').sine(100).play();

There is a shorthand for creating a new FacetPattern instance:

$('example').sine(100).play();

Some FacetPatterns might contain other FacetPatterns. The most outer-facing one must have a name via the above method $(), but other FacetPatterns inside the code can use a separate, more concise shorthand, _:

$('example').sine(100).times(_.sine(100)).play();

There are lots of methods to generate, translate, and orchestrate playback on FacetPattern data:

$('example').sine(100).times(random()).play();
// each time you run ^, the same sine wave at a different volume

Certain operations (e.g. sometimes(), iter(), slices(), mix(), run()) allow you to supply functions as arguments:

$('example').iter(16,()=>{this.append(_.randsamp('808').speed(10))}).play();
// stitches together 16 random samples, each playing at 10x normal speed

In-browser text editor and user interface

Below the text editor, there are several UI elements which control the servers running in the background. Moving from left to right:


Key-combination shortcuts


Running "npm run facet"

When you run the npm run facet command in the terminal, the following sequence of events occurs:

A server, known as the process manager, starts up on http://localhost:5831. This server is responsible for managing the startup and shutdown of the two servers listed below:

  1. The transport server starts up on http://localhost:3211. This server is responsible for handling the timing and playback of audio, MIDI, and OSC events.

  2. The pattern generator server starts up on http://localhost:1123. This server listens to requests from the text editor UI in the browser located at http://localhost:1124 and interprets those commands into data. If the pattern is intended to be played back as audio, a corresponding .wav file will be stored in the tmp/ subdirectory in the Facet repo. Otherwise, if the pattern is intended for MIDI or OSC output, the data will be posted directly to the transport server.


Configuration for Max

While Facet runs completely as a standalone process, it is possible to send data from Facet into a Max patch using a custom facet.maxpat object which is included in this repository. First, follow these setup steps:

  1. Open Max. In the Max navbar, go to > Options > File Preferences, click "Add Path", and add the facet directory (the folder that contains this file). Make sure that the Subfolders checkbox is checked.
  2. Create a new Max patcher, and add a facet object.
  3. The single outlet in the facet object will pass OSC commands from Facet into your patcher. Use the /route Max object to route the OSC data in your Max patcher.
  4. See the examples/osc.md example file for more details on receiving OSC in Max or any other application.

Global event resolution

By default, Facet checks every 10 milliseconds whether it needs to fire any events that produce output, such as playing audio, MIDI, or osc. You can change EVENT_RESOLUTION_MS in js/config.js to set a different integer value. Slower speeds (e.g. 20 = 20ms) will produce less tightly-timed events but can help make it possible for Facet to run on computers with less CPU resources, at the expense of slight timing accuracy. Faster speeds (e.g. 4 = 4ms) will produce tighter event scheduling but can overload computers with less CPU resources.


Command reference

The rest of this document lists all the methods and variables available in Facet. Since Facet is an extension of JavaScript, you can also incoporate core functions such as Math.pow(), Date.now(), etc.


Variables

Variables output a single floating-point number that can be used as an argument to another method.


mousex / mousey

Both mousex and mousey, as floating-point number representations of your cursor's position in the browser window, are available for use in commands, e.g.:

$('example').sine(100).times(mousey).play();
// cursor y position controls volume every time the code runs

notevalues

There are 128 "notevalues" variables, corresponding to the number of audio samples relative to geometric divisions of one transport loop (a "whole note"). A whole note is n1, a half note is n2, etc... up to n128.

$('example').noise(n1);
// ^ at 120bpm 4/4  = 2 second transport loop = 88200 samples
// ^ at 120bpm 3/4  = 1.5 second transport loop = 66150 samples

$('example').noise(n128);
// ^ at 120bpm 4/4 = 517 samples

bpm

The variable bpm represents the current BPM in the Facet transport when the FacetPattern is generated.


bars

The variable bars represents how many loops have occurred since the time the server was started. This is especially useful with the modulo % operator, e.g.: bars%4, which could be either 0, 1, 2, or 3, depending on how many loops have occurred.


time signature

The variables time_num and time_denom represent the current time signature of the Facet transport. These variables will update when you modify either the time signature numerator or time signature denominator number inputs in the user interface, or if the time() method is dynamically updating the time signature.


sample rate

The sample rate for audio generated and played back with Facet can be changed by modifying SAMPLE_RATE in js/config.js to any integer.

In Facet commands, the constant SAMPLE_RATE refers to the configured sample rate, which is useful for doing something for a specific number of seconds. The constant NYQUIST refers to the Nyquist frequency which is SAMPLE_RATE/2.

$('example').noise(SAMPLE_RATE).play();
// generate and continually play back exactly 1 second of noise

Outputs

Facet can synthesize and orchestrate the playback of multiple FacetPatterns simultaneously, producing audio, MIDI, or OSC output. By default, patterns will continually regenerate each loop, but methods such as .whenmod(), .keep(), and .once() modify this behavior.


Audio output

play ( PlaybackFacetPattern, pitchSequenceData = 1 )

    $('example')
      .randsamp('808')
      .play();
    // plays once at beginning of loop
    $('example')
      .randsamp('808')
      .play(0.5);
    // plays once at middle point
    $('example')
      .randsamp('808')
      .play(_.noise(4)
        .scale(0, 1));
    // plays once at 4 random positions
    $('example')
      .randsamp('808')
      .play(_.noise(4)
        .scale(0, 1), _.ramp(1, 4, 4));
    // plays once at 4 random positions, each with a higher pitch

pan ( PanningFacetPattern = 0 )

$('example')
  .noise(n1)
  .times(_.ramp(1, 0, n1))
  .pan(_.sine(1, n1)
    .scale(0, 1))
  .play();
// no channels are specified; defaults to stereo panning
$('example')
  .noise(n1)
  .times(_.ramp(1, 0, n1))
  .channels([1, 2, 4])
  .pan(_.sine(1, n1)
    .scale(0, 1))
  .play();
// pans the noise smoothly around channels 1, 2, and 4

channel ( channels )

	$('example')
      .randsamp('808')
      .channel(1)
      .play();
    // first channel only
    $('example')
      .randsamp('808')
      .channels([1, 3])
      .play();
    // channels 1 & 3 only
    $('example')
      .randsamp('808')
      .channel(_.from([9, 10, 11, 12, 13, 14, 15, 16])
        .shuffle()
        .reduce(ri(1, 8)))
      .play();
    // play on a random number of channels from 9-16

saveas ( filename )

	$('example')
      .iter(6, () => {
          this.append(_.sine(ri(1, 40)))
            .saveas('/myNoiseStuff/' + Date.now())})
            .once();
            // creates 6 wav files in the myNoiseStuff directory.
            // each filename is the UNIX timestamp to preserve order.

stop ( )

$('example')
  .noise(n16)
  .play()
  .stop();
// you only hear sound when you remove the stop()

MIDI / OSC output

Facet has various methods for generating and outputting MIDI and OSC data to control other synthesizers or DAWs. You need to connect the MIDI device you want to use before starting Facet.

note ( VelocityPattern = 100, DurationPattern = 125, channel = 1, PositionPattern )

	$('example')
      .sine(1)
      .size(32)
      .scale(36, 90)
      .round()
      .note();
    // sine wave of 32 notes going from 36 to 90 and back each loop
    $('example')
      .noise(16)
      .scale(60, 80)
      .sort()
      .note(100, 125, 1, _.ramp(0, 0.25, 16));
    // play all notes during the first 25% of the whole note

note2d ( VelocityPattern = 100, DurationPattern = 125, channel = 1, lowNote = 0, highNote = 127 )

$('example')
  .silence(2500)
  .circle2d(25, 25, 25, 1)
  .saveimg()
  .note2d(100, 125, 1, 30, 80)
  .once();
// saves 50x50 image with a circle in the middle; then plays that circle shape between the MIDI notes 30 and 80

cc ( controller_number = 70, channel = 1 )

$('example').drunk(64,0.1).cc();
// sends out cc channel 70
$('example2').drunk(64,0.1).cc(71);
// sends out cc channel 71

chord ( chordTypePattern, inversion_mode = 0 )

'maj' / 'major'             =   [0,4,7]
'min' / 'minor'             =   [0,3,7]
'fifth' / '5th'              =   [0,5]
'seventh' / '7th'           =   [0,4,7,10]
'major seventh' / 'maj7'    =   [0,4,7,11]
'minor seventh' / 'm7'      =   [0,3,7,10]
'diminished' / 'dim'        =   [-1,2,5]
'add2'                      =   [0,2,4,7]
'add9'                      =   [0,4,7,14]
	$('example')
      .ramp(36, 72, 32)
      .chord('maj7')
      .add((bars % 4) * 12)
      .key('F#', 'major')
      .note(50, 100, 1);
    // same maj7 chord run, getting an octave higher for 4 octaves then resets
    $('example')
      .noise(16)
      .scale(36, 90)
      .chord(_.from([3, 5, 7, 10, 11, 14, 16, 20, 25]))
      .key('c', 'major')
      .note();
    // 9-note chords mapped onto c major
    $('example')
      .noise(8)
      .scale(30, 80)
      .chord('maj7')
      .key(['c', 'f#'], ['major', 'minor'])
      .note(100, 500);
    // maj7 chords, first in c major, then in f# minor

key ( keyLetterPattern, keyScalePattern )

["major pentatonic", "major", "minor", "major blues", "minor blues", "melodic minor", "harmonic minor", "bebop", "diminished", "dorian", "lydian", "mixolydian", "phrygian", "locrian", "ionian pentatonic", "mixolydian pentatonic", "ritusen", "egyptian", "neopolitan major pentatonic", "vietnamese 1", "pelog", "kumoijoshi", "hirajoshi", "iwato", "in-sen", "lydian pentatonic", "malkos raga", "locrian pentatonic", "minor pentatonic", "minor six pentatonic", "flat three pentatonic", "flat six pentatonic", "scriabin", "whole tone pentatonic", "lydian #5P pentatonic", "lydian dominant pentatonic", "minor #7M pentatonic", "super locrian pentatonic", "minor hexatonic", "augmented", "piongio", "prometheus neopolitan", "prometheus", "mystery #1", "six tone symmetric", "whole tone", "messiaen's mode #5", "locrian major", "double harmonic lydian", "altered", "locrian #2", "mixolydian b6", "lydian dominant", "lydian augmented", "dorian b2", "ultralocrian", "locrian 6", "augmented heptatonic", "dorian #4", "lydian diminished", "leading whole tone", "lydian minor", "phrygian dominant", "balinese", "neopolitan major", "harmonic major", "double harmonic major", "hungarian minor", "hungarian major", "oriental", "flamenco", "todi raga", "persian", "enigmatic", "major augmented", "lydian #9", "messiaen's mode #4", "purvi raga", "spanish heptatonic", "bebop minor", "bebop major", "bebop locrian", "minor bebop", "ichikosucho", "minor six diminished", "half-whole diminished", "kafi raga", "messiaen's mode #6", "composite blues", "messiaen's mode #3", "messiaen's mode #7", "chromatic"]
$('example')
  .randsamp('808')
  .reduce(32)
  .scale(36, 51)
  .key('F#', 'bebop')
  .note();
$('example')
  .noise(16)
  .scale(30, 80)
  .key('c', _.from([1]))
  .note();
// octave scale, via custom FacetPattern
$('example')
  .noise(16)
  .scale(30, 80)
  .key('c', _.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
  .note();
// equivalent to the above custom octave scale; the padded zeroes are optional
$('example')
  .noise(16)
  .scale(30, 80)
  .key('c', _.from([1, 0, 0, 0, 0, 0, 1]))
  .note();
// octave + perfect fifth scale, via custom FacetPattern
$('example')
  .noise(16)
  .scale(30, 80)
  .key(['c', 'f#'], ['major', 'minor'])
  .note();
// the first half is c major; the second half is f# major

osc ( address )

$('example').noise(128).osc('/test');

pitchbend ( channel = 1 )

$('example').sine(1).size(128).pitchbend();

savemidi (midifilename = Date.now(), velocityPattern = 64, durationPattern = 16, wraps = 1, tick_mode = false_)

	'1'   =   whole note
	'2'   =   half note
	'd2'  =   dotted half note
	'dd2' =   double dotted half note
	'4'   =   quarter note
	'4t'  =   quarter triplet note
	'd4'  =   dotted quarter note
	'dd4' =   double dotted quarter note
	'8'   =   eighth note
	'8t'  =   eighth triplet note
	'd8'  =   dotted eighth note
	'dd8' =   double dotted eighth note
	'16'  =   sixteenth note
	'16t' =   sixteenth triplet note
	'32'  =   thirty-second note
	'64'  =   sixty-fourth note
$('example')
  .noise(64)
  .scale(20, 90)
  .key('c major')
  .savemidi(ts(), 64, 16)
  .once();
// 64 random notes in c major at 64 velocity, each lasting a 16th note
$('example')
  .noise(64)
  .scale(20, 90)
  .key('c major')
  .savemidi(ts(), _.noise(64)
    .scale(1, 100), 4, 1, true)
  .once();
// 64 random notes in c major, each with a random velocity between 1 - 100, each lasting 4 ticks
$('example')
  .iter(8, () => {
    this.append(_.sine(choose([1, 2, 3, 4]))
      .size(128)
      .scale(ri(30, 50), ri(60, 90))
      .key('c major'))
  })
  .savemidi(ts(), 64, 16, 8)
  .once();
// 8 sine wave patterns playing notes in c major, superposed on top of each other. try changing the wraps argument to values other than 8

savemidi2d() (midifilename = Date.now(), velocityPattern = 64, durationPattern = 16, tick_mode = false_, min_note = 0, max_note = 127)

		'1'   =   whole note
		'2'   =   half note
		'd2'  =   dotted half note
		'dd2' =   double dotted half note
		'4'   =   quarter note
		'4t'  =   quarter triplet note
		'd4'  =   dotted quarter note
		'dd4' =   double dotted quarter note
		'8'   =   eighth note
		'8t'  =   eighth triplet note
		'd8'  =   dotted eighth note
		'dd8' =   double dotted eighth note
		'16'  =   sixteenth note
		'16t' =   sixteenth triplet note
		'32'  =   thirty-second note
		'64'  =   sixty-fourth note
$('example')
  .silence(2500)
  .iter(8, () => {
    this.tri2d(ri(0, 49), ri(0, 49), ri(0, 49), ri(0, 49), ri(0, 49), ri(0, 49), 1, 0)
  })
  .savemidi2d(ts(), 64, 16)
  .once();
  // 8 randomly sized triangles in 2d space, all at velocity 64, 16th note durations
$('example')
  .silence(2500)
  .iter(10, () => {
    this.circle2d(ri(10, 40), ri(10, 40), 10, 1, 0)
  })
  .savemidi2d(ts(), 64, 64)
  .once();
  // 10 randomly sized circles in 2d space, all at velocity 64, 64th note durations

Methods for controlling time

bpm ( bpm_pattern )

$('example')
  .bpm(_.from([20, 40, 80, 160, 320])
    .shuffle()); // each loop will be all 5 of these BPM, randomly ordered
$('example')
  .bpm(_.ramp(20, 200, 64)
    .over(4)); // ramps from 20BPM to 200BPM over 4 loops

time ( time_signature_numerator_pattern = 4, time_signature_denominator_pattern = 4 )

$('example')
  .time(4, 4); // set time to 4/4
$('example')
  .time(_.ramp(8, 1, 8)
    .over(8), 2); // ramps from 8/2 to 1/2 time over 8 loops

Methods for controlling pattern regeneration

whenmod ( modulo_operand, equals_value = 0 )

$('example_0')
  .sine(ri(100, 400))
  .whenmod(4, 0); // regenerates the pattern once every 4 bars
$('example_2')
  .sine(ri(100, 400))
  .whenmod(4, 2); // regenerates the pattern once every 4 bars, but 2 bars away from the above example

keep ( )

$('example')
  .sine(ri(10, 500))
  .keep()
  .play();

once ( )

$('example')
  .noise(4096)
  .play()
  .once();

over ( n_loops = 1 )

$('example')
  .randsamp('808')
  .play(_.ramp(0, 1, 16))
  .over(ri(1, 4)); // random sample played 16 times over 1,2,3 or 4 bars
$('example')
  .drunk(2048, 0.01)
  .cc()
  .over(128); // drunk walk over 128 bars, creates a drifty process that you can map onto device paramters to slowly randomize something

Methods for setting variables

These can be useful when you want to access or modify the same pattern across commands or inside of one command.

set ( name )

$('set_example')
  .noise(8)
  .scale(0, 1)
  .set('my_var')
  .once();
  // first, set the variable here

$('example')
  .sine(100)
  .times(my_var)
  .play();
  // now, you can use my_var in commands

drift ( seedPattern, patternName, command = function )

$('example')
  .drift(_.noise(16)
    .scale(36, 72)
    .sort(), 'mynotes', () => {
      this.walk(0.1, 1)
    })
  .note();
  // starts as ascending melody and drifts away into randomness

inc ( name, amount_to_add = 1 )

$('example')
  .inc('abc')
  .iter(abc, () => {
    this.sup(_.randsamp('808'), i / iters)
  })
  .play();
  // more 808 samples each iteration

dec ( name, amount_to_subtract = 1 )

$('example')
  .from(8)
  .set('abc')
  .sometimes(0.5, () => {
    this.dec('abc')
  })
  .sometimes(0.5, () => {
    this.inc('abc')
  })
  .iter(abc, () => {
    this.sup(_.randsamp('k'), i / iters)
  })
  .play();
  // start at 8, sometimes increment & sometimes decrement the total number of 808 samples

setlocal ( name )

$('example')
  .drunk(1000, 0.2)
  .setlocal('mylocalpattern')
  .reduce(0)
  .iter(1000, () => {
    this.append(_.getlocal('mylocalpattern')
      .jam(0.1, 0.1)
      .setlocal('mylocalpattern'))
  })
  .saveimg()
  .once();
  // initial 1000-value drunk walk, each row 10% of the pixels are +/- 10% modified from previous row

getlocal ( name )

$('example')
  .drunk(1000, 0.2)
  .setlocal('mylocalpattern')
  .reduce(0)
  .iter(1000, () => {
    this.append(_.getlocal('mylocalpattern')
      .jam(0.1, 0.1)
      .setlocal('mylocalpattern'))
  })
  .saveimg()
  .once();
  // initial 1000-value drunk walk, each row 10% of the pixels are +/- 10% modified from previous row

Utility functions

barmod ( modulo, values )

$('example')
  .sine(barmod(4, [0, 100, 1, 150, 2, 200, 3, 300]))
  .play();
  // when bars % 4 == 0, plays a 100Hz sine.
  // when bars % 4 == 1, plays a 150 Hz sine.
  // when bars % 4 == 2, plays a 200Hz sine.
  // when bars % 4 == 3, plays a 300Hz sine.

choose ( pattern )

$('example')
  .sine(choose([10, 200, 1000]))
  .play();
  // sine wave with either 10, 200, or 1000 cycles

cof ( index )

$('example')
  .noise(16)
  .scale(36, 90)
  .key(cof(2))
  .note();
  // MIDI notes in D major
$('example')
  .noise(16)
  .scale(36, 90)
  .key(cof(ri(0, 11)))
  .note();
  // MIDI notes in random major key

decide ()

$('example')
  .sine(choose([10, 200, 1000]))
  .dup(decide())
  .play();
  // duplicate half the time

ftom ( hzfrequency )

$('example')
  .from(440)
  .ftom()
  .note();
  // plays an A440 MIDI note

ms ( milliseconds )

$('example')
  .noise(4096)
  .size(ms(5))
  .play();
  // 5ms noise
$('example')
  .noise(4096)
  .size(ms(50))
  .play();
  // 50ms noise

mtof ( midi_note_number )

$('example')
  .sine(mtof(choose([36, 38, 40, 41, 43, 45, 47, 48])))
  .play();
  // random sine wave each loop in C major key

mtos ( midi_note_number )

$('example')
  .noise(n4)
  .delay(mtos(choose([36, 38, 40, 41, 43, 45, 47, 48])))
  .delay(mtos(choose([36, 38, 40, 41, 43, 45, 47, 48])))
  .delay(mtos(choose([36, 38, 40, 41, 43, 45, 47, 48])))
  .play();
  // noise is delayed by amounts that are harmonic with C major key

random ( min = 0, max = 1, int_mode = 0, weight = 1 )

$('example')
  .sine(ri(20, 1000))
  .play();
  // a sine wave with 20 - 1000 cycles

ts ( )

$('example')
  .sine(100, n1)
  .saveas('mytest' + ts())
  .once();
  // saves a file in the samples directory named like this: mytest1704420621454.wav

just ()

$('example')
  .sine(100, n16)
  .play(_.ramp(0, 1, 12), _.from(just()));
  // sine waves in ascending just intonation scale

pythagorean ()

$('example')
  .sine(100, n16)
  .play(_.ramp(0, 1, 12), _.from(pythagorean()));
  // sine waves in ascending pythagorean intonation scale

equaltemp ()

$('example')
  .sine(100, n16)
  .play(_.ramp(0, 1, 12), _.from(equaltemp()));
  // sine waves in ascending equal temperament intonation scale

meantone ()

$('example')
  .sine(100, n16)
  .play(_.ramp(0, 1, 12), _.from(meantone()));
  // sine waves in ascending meantone intonation scale

edo19 ()

$('example')
  .sine(100, n16)
  .play(_.ramp(0, 1, 12), _.from(edo19()));
  // sine waves in ascending edo19 intonation scale

edo31 ()

$('example')
  .sine(100, n16)
  .play(_.ramp(0, 1, 12), _.from(edo31()));
  // sine waves in ascending edo31 intonation scale

Methods for controlling MIDI scales

randscale ( )

$('example')
  .noise(32)
  .scale(30, 80)
  .sort()
  .key('f#', randscale())
  .note();
  // random scale in f#

FacetPattern generators that can take a FacetPattern, number, array, or object as an argument

When a generator takes a FacetPattern or an array as an argument, it uses that pattern to dynamically change its behavior over time, affecting the output in a more complex way than if a single number were supplied. For example, with the command $('example').sine(440).play();, the output is a static 440Hz wave. But with the command $('example').sine(_.sine(5).scale(20,2000))).play();, the frequency of the sine wave is being modulated by a 5 Hz sine wave which is generating values between 20 and 2000. This produces a classic frequency modulation sound, but since you can supply any FacetPattern as an argument, there are lots of sound design possibilities.

sine ( frequencyPattern, duration = sample_rate, fade_in_and_out = true )

$('example')
  .sine(440, n4)
  .play();
  // 440 Hz sine wave for a quarter note
$('example')
  .sine(_.ramp(10, 2000, 300))
  .play();
  // ramp from 10Hz to 2000 Hz over 300 values
$('example')
  .sine(_.sine(5)
    .scale(20, 2000))
  .play();
  // 5Hz frequency modulation with output frequencies oscillating between 20Hz and 2000Hz

cosine ( frequencyPattern, duration = 1 second, fade_in_and_out = true )

$('example')
  .cosine(440, n4)
  .play();
  // 440 Hz cosine wave for a quarter note
$('example')
  .cosine(_.ramp(10, 2000, 300))
  .play();
  // ramp from 10Hz to 2000 Hz over 300 values
$('example')
  .cosine(_.cosine(5)
    .scale(20, 2000))
  .play();
  // 5Hz frequency modulation with output frequencies oscillating between 20Hz and 2000Hz

circle ( frequencyPattern, duration = 1 second )

$('example')
  .circle(440, n4)
  .play();
  // 440 Hz circle wave for a quarter note
$('example')
  .noise(n1)
  .times(_.circle(4))
  .play()
  .once();
  // amplitude modulation of noise with a quarter note circular waveform
$('example')
  .noise(n1)
  .ffilter(_.circle(1)
    .invert()
    .size(128)
    .scale(0, NYQUIST / 2), _.circle(1)
    .size(128)
    .scale(NYQUIST / 2, NYQUIST))
  .play()
  .once();
  // circular spectral filtering of a whole note of noise

markov ( states = [{name, value, probs}, {name, value, probs}, ...] )

- modifies the input FacetPattern according to a Markov chain. The `states` parameter is an array where each entry is an object representing a state. Each state object has a `name`, a `value` that corresponds to a value in `this.data`, and a `probs` object that defines the transition probabilities to other states.
- the method modifies `this.data` by transitioning each value to a new state based on the probabilities defined in the `states` array. The transition probabilities are normalized so that they add up to 1 for each state.
- example: 
$('example')
.iter(choose([3,4,8]), () => {
this.prepend(_.from(['k*', 'h*', 's*', 'h*', '_', '_','_', '_'])
  .markov([{
      name: "state1",
      value: 'k*',
      probs: {
        "state1": 0.8,
        "state2": 0.5,
        "state3": 0.5
      }
    },
    {
      name: "state2",
      value: 'h*',
      probs: {
        "state3": 1
      }
    },
    {
      name: "state3",
      value: 's*',
      probs: {
        "state1": 1
      }
    },
	{
      name: "state4",
      value: '_',
      probs: {
        "state1": 0.5,
		"state4": 0.5
      }
    }
  ]))
})
.run(() => {
this.seq(this.data)
}).play();

phasor ( frequencyPattern, duration = 1 second, fade_in_and_out = true )

$('example')
  .phasor(440, n4)
  .play();
  // 440 Hz phasor wave for a quarter note
$('example')
  .phasor(_.ramp(10, 2000, 300))
  .play();
  // ramp from 10Hz to 2000 Hz over 300 values
$('example')
  .phasor(_.phasor(5)
    .scale(20, 2000))
  .play();
  // 5Hz frequency modulation with output frequencies oscillating between 20Hz and 2000Hz

rect ( frequencyPattern, duration = 1 second, pulse_width = 0.5, fade_in_and_out = true )

$('example')
  .rect(440, n4, rf())
  .play();
  // 440 Hz rectangle wave for a quarter note, different bandwidth each time
$('example')
  .rect(_.ramp(10, 2000, 300))
  .play();
  // ramp from 10Hz to 2000 Hz over 300 values
$('example')
  .rect(_.rect(5)
    .scale(20, 2000))
  .play();
  // 5Hz frequency modulation with output frequencies oscillating between 20Hz and 2000Hz

square ( frequencyPattern, duration = sample_rate )

$('example')
  .square(440, n4)
  .play();
  // 440 Hz square wave for a quarter note
$('example')
  .square(_.ramp(10, 2000, 300))
  .play();
  // ramp from 10Hz to 2000 Hz over 300 values
$('example')
  .square(_.square(5)
    .scale(20, 2000))
  .play();
  // 5Hz frequency modulation with output frequencies oscillating between 20Hz and 2000Hz

tri ( frequencyPattern, duration = sample_rate, fade_in_and_out = true )

$('example')
  .tri(440, n4)
  .play();
  // 440 Hz triangle wave for a quarter note
$('example')
  .tri(_.ramp(10, 2000, 300))
  .play();
  // ramp from 10Hz to 2000 Hz over 300 values
$('example')
  .tri(_.tri(5)
    .scale(20, 2000))
  .play();
  // 5Hz frequency modulation with output frequencies oscillating between 20Hz and 2000Hz

FacetPattern generators

binary ( integer, length)

$('example')
  .binary(8);
  // 1000
$('example')
  .binary(490321, 13);
  // 1110111101101: truncated at 13 values
$('example')
  .binary(8, 12);
  // 000000001000: padded with 0s

dirsamp ( dir = ../samples/, dir_pos, channel_index = 0 )

$('example')
  .iter(8, () => {
    this.sup(_.dirsamp('808', i / iters), i / iters)
  })
  .play();
  // 808 sequence

drunk ( length, intensity, starting_value = Math.random() )

$('example')
  .drunk(16, 0.1);
  // slight random movement

envelope ( values )

$('example')
  .noise(ms(500))
  .times(_.envelope([0, 1, ms(10), 1, 0.1, ms(200), 0.1, 0, ms(290)]))
  .play();
  // transient noise burst

euclid ( pulses, steps )

$('example')
  .sine(100)
  .times(_.euclid(4, 8))
  .play();
  // gating a sine wave with a euclidean sequence

file ( filepath )

$('example')
  .file('my_image.png')
  .play();
  // if my_image.png is in the files directory, this will play the file's raw data. NOTE: this could be very noisy!
$('example')
  .file('/Users/my_username/Desktop/myfile.zip')
  .play();
  // example with a supplied absolute file path

from ( pattern )

$('example')
  .from([1, 2, 3, 4]);

image ( filepath, columnsPerSecond = 512, minimumFrequency = 20, maximumFrequency = sample_rate / 2, frequencyPattern )

$('example')
  .image('/path/to/file/goes/here.png', 1024)
  .play();
  // each column lasts 1024 samples

noise ( length )

$('example')
  .noise(1024)
  .play();

pluck ( frequency, damping = 0, feedback = 0.5 )

$('example')
  .pluck(440, rf(), rf())
  .play(); // different 440 Hz quarter note pluck each time

primes ( n, offset_from_first_prime = 2, skip = 1 )

$('example')
  .noise(n4)
  .times(_.ramp(1, 0, n4))
  .iter(12, () => {
    this.allpass()
      .delay(_.primes(60, 1000, ri(20, 2000))
        .data[i])
      .full()
  })
  .full()
  .play();
  // generates a quarter note transient burst of noise, then iteratively sends it through delays that are all primes

ramp ( from, to, size = 128 )

$('example')
  .ramp(250, 100, 1000);
  // go from 250 to 100 over 1000 values

randfile ( dir = ../files/ )

$('example')
  .randfile()
  .play();
  // random new file converted to audio every time

randsamp ( dir = ../samples/ channel_index = 0 )

$('example')
  .randsamp('808')
  .reverse()
  .play();
  // random backwards sample

sample ( filepath, channel_index = 0)

$('example')
  .sample('1234')
  .play();
  // if 1234.wav is in the samples directory, you're good to go
$('example')
  .sample('./myfolder/myfile.wav');
  // or point to the file with a relative path

silence ( length )

$('example')
  .silence(n2)
  .append(_.noise(n2))
  .play();
  // first half of loop is silence; second half is noise

spiral ( length, degrees = 360/length, angle_phase_offset = 0 )

$('example')
  .sine(1)
  .times(_.spiral(1000, ri(1, 360)))
  .play();
  // a 1Hz sine wave with amplitude modulated by a spiral

turing ( length )

$('example')
  .turing(64); // instant rhythmic triggers

FacetPattern modulators

abs ( )

$('example')
  .sine(100)
  .add(-0.3)
  .abs()
  .play();
  // a wonky sine

allpass ( frequency = default_sample_rate/2 )

 $('example')
  .randsamp('808')
  .iter(12, () => {
    this.allpass()
      .delay(ri(1, 6000))
  })
  .scale(-1, 1)
  .play(); // reverb

at ( position, value )

$('example')
  .turing(16)
  .at(0, 1);
  // the 1st value of the 16-step Turing sequence (i.e. 0% position) is always 1
$('example')
  .turing(16)
  .at(0.5, 2);
  // the 9th value of the 16-step Turing sequence (i.e. 50% position) is always 2

audio ( )

$('example')
  .sine(440)
  .add(0.9)
  .audio()
  .play();
  // .audio() removes the DC offset that is added by the .add() command

bitshift ( shift = 16 )

$('example')
  .sine(1000, n2)
  .bitshift(16)
  .play();
  // rotates the bits of a 1000Hz sine wave by 16 positions

changed ( )

$('example')
  .from([1, 1, 3, 4])
  .changed(); // 1 0 1 1

clip ( min, max )

$('example')
  .from([1, 2, 3, 4])
  .clip(2, 3);
  // 2 2 3 3

compress ( ratio, threshold, attackTime, releaseTime )

$('example')
  .randsamp('808')
  .compress(0.1, 0.001, 0.01, 0.01)
  .play();

crab ( )

$('example')
  .sine(_.ramp(20, 2000, 1000))
  .crab()
  .full()
  .play();
  // sine wave ramps from 20Hz to 2000Hz both backwards and forwards at the same time

curve ( tension = 0.5, segments = 25 )

$('example')
  .noise(16)
  .curve(); // not so noisy
$('example')
  .noise(16)
  .curve(0.5, 10); // fewer segments per curve
$('example')
  .noise(16)
  .curve(0.9); // different curve type

distavg ( )

$('example')
  .from([0.1, 4, 3.14])
  .distavg();
  // -2.3133 1.5867 0.7267

dup ( num )

$('example')
  .noise(n16)
  .dup(ri(2, 12))
  .play();
  // 16th note of noise repeats between 2 and 12 times each loop 

echo ( num, feedback = 0.666 )

$('example')
  .from([1])
  .echo(5);
  // 1 0.666 0.4435 0.29540 0.19674 0.13103
$('example')
  .phasor(50)
  .size(n8)
  .echo(7)
  .play();
  // echoing out over a whole note 

expo ( exponent )

$('example')
  .sine(_.ramp(100, 2000, 512)
    .expo(6), n1)
  .play()
  .once();
  // experiment with different expo() values to hear the difference

fade ( fade_percent = 0.1 )

$('example')
  .noise(1024)
  .fade()
  .play();

fadein ( fade_percent = 0.5 )

$('example')
  .noise(20000)
  .fadein()
  .play();

fadeout ( fade_percent = 0.5 )

$('example')
  .noise(20000)
  .fadeout()
  .play();

fkey ( MIDI_note_scale, binThreshold = 0.005, maxHarmonic = 10 )

$('example')
  .noise(n1)
  .times(_.ramp(1, 0, n1))
  .fkey(_.from([48, 50, 52, 53, 55, 57, 59, 60]), 0.005, 6)
  .play();
  // noise spectrally filtered to bins matching C major notes 48,50,52,53,55,57,59,60 and their 6 next harmonics

flange ( delaySamples = 220, depth = 110 )

$('example')
  .sine(100, n1)
  .flange(220, 110)
  .play();
  // flanged whole note sine wave at 100Hz

flipabove ( maximum )

$('example')
  .sine(100)
  .flipabove(0.2)
  .play();
  // wonky sine

flipbelow ( min )

$('example')
  .sine(100)
  .flipbelow(0.2)
  .play();
  // inverse wonky sine

follow ( attackTime = default_sample_rate / 10, releaseTime = default_sample_rate / 4 )

$('example')
  .noise(n1)
  .times(_.noise(32)
    .scale(0, 1)
    .size(n1)
    .follow(n16, n16))
  .play();
  // controlling the amplitude of a whole note of noise, with 32 samples of noise sent through the envelope follower

fracture ( pieces )

$('example')
  .sine(100)
  .fracture(10)
  .play();
  // the sine has shattered into 10 pieces!

ftom ( )

$('example')
  .ramp(1000, 250, 16)
  .ftom();
  // 83, 82, 82, 81, 80, 79, 77, 76, 75, 74, 72, 71, 69, 67, 65, 62

full ( )

$('example')
  .noise(n2)
  .times(0.1)
  .loud()
  .play();
  // remove loud() to hear the difference

gate ( threshold, attackSamples, releaseSamples )

$('example')
  .sine(50)
  .gate(0.1, 20, 20)
  .play();

gt ( amt )

$('example')
  .from([0.1, 0.3, 0.5, 0.7])
  .gt(0.6);
  // 0 0 0 1

gte ( amt )

$('example')
  .from([0.1, 0.3, 0.5, 0.7])
  .gte(0.5);
  // 0 0 1 1

interp ( weight = 0.5, name )

$('example')
  .sine(100)
  .interp(0.5, _.randsamp('808'))
  .play();
  // 50% sine wave; 50% random sample

invert ( )

$('example')
  .from([0, 0.1, 0.5, 0.667, 1])
  .invert();
  // 1 0.9 0.5 0.333 0

jam ( prob, amt )

$('example')
  .drunk(128, 0.05)
  .jam(0.1, 0.7);
  // small 128 step random walk with larger deviations from the jam

lt ( amt )

$('example')
  .from([0.1, 0.3, 0.5, 0.7])
  .lt(0.6);
  // 1 1 0 0

lte ( amt )

$('example')
  .from([0.1, 0.3, 0.5, 0.7])
  .lte(0.5);
  // 1 1 1 0

log ( intensity , direction )

$('example')
  .noise(n8)
  .log(rf())
  .play();
  // each time a different logarithmic curve on the 8th note of noise

mtof ( )

$('example')
  .from([60, 55, 76, 100])
  .mtof();
  // 261.63, 220, 659.26, 2637.02

mtos ( )

$('example')
  .noise(n4)
  .comb(_.noise(128)
    .scale(0, 127)
    .key('c', 'major')
    .mtos()
    .sort())
  .play();
  // comb filter delayed by sample values in c major on a quarter note of noise

modulo ( amt )

$('example')
  .from([1, 2, 3, 4])
  .modulo(3);
  // 1 2 0 1

mutechunks ( chunks, prob, yes_fade = true )

$('example')
  .randsamp('808')
  .mutechunks(16, 0.33)
  .play();
  // 33% of 16 audio slices muted

normalize ( )

$('example')
  .sine(1)
  .times(4000)
  .normalize();
  // the *4000 gain is undone!
$('example')
  .sine(1)
  .scale(-10, 10)
  .normalize();
  // works with negative values

nonzero ( )

$('example')
  .from([1, 2, 3, 4])
  .prob(0.5)
  .nonzero();
  // if 2 and 4 are set to 0 by prob(0.5), the output of .nonzero() would be 1 1 3 3

palindrome ( )

$('example')
  .from([0, 1, 2, 3])
  .palindrome();
  // 0 1 2 3 3 2 1 0

pow ( expo, direction = 1 )

$('example')
  .sine(100)
  .pow(6.5)
  .play();
  // squished into the end
$('example')
  .sine(100)
  .pow(6.5, -1)
  .play();
  // squished at the beginning

prob ( amt )

$('example')
  .from([1, 2, 3, 4])
  .prob(0.5);
  // 1 0 3 0 first time it runs
$('example')
  .from([1, 2, 3, 4])
  .prob(0.5);
  // 0 0 3 4 second time it runs
$('example')
  .from([1, 2, 3, 4])
  .prob(0.5);
  // 0 2 3 4 third time it runs

quantize ( resolution )

$('example')
  .drunk(16, 0.5)
  .quantize(4);
  // 0.5241 0 0 0 0.7420 0 0 0 1.0 0 0 0 0.4268 0 0 0

range ( new_start, new_end )

$('example')
  .from([0.1, 0.2, 0.3, 0.4])
  .range(0.5, 1);
  // 0.3 0.4

rangesamps ( start, length )

$('example')
  .sine(n1)
  .log(0.9)
  .rangesamps(rf(0, 0.875), n8)
  .play();
  // plays a different 8th note from the same de-pitched sine wave every time
$('example')
  .silence(n1)
  .iter(128, () => {
    this.sup(_.noise(n64)
      .lpf(_.ramp(250, 40, 20), 50)
      .times(_.ramp(1, 0, n64))
      .rangesamps(rf(), n64)
      .fade(0.1), rf())
  })
  .play();
  // granular synthesis of 128 synthesized kick drums

rechunk ( chunks, probability )

$('example')
  .randsamp('808')
  .rechunk(16)
  .play();
  // 16 slices from the sample in random order

reduce ( new_size )

$('example')
  .from([1, 2, 3, 4])
  .reduce(2);
  // 1 3

replace ( original_value, new_value )

$('example')
  .from([42, 0, 0, 36])
  .replace(0, -1);
  // 42,-1,-1,36

resonate ( baseFrequency, coefficients, q = 80, wet = 1 )

$('example')
  .noise(n16)
  .times(_.ramp(1, 0, n16))
  .resonate(mtof(36), _.ramp(1, 20, 20), 80)
  .play();
  // 16th note transient noise burst, resonating at its first 20 harmonics starting at 65.41 Hz (MIDI note C2, mtof(36))

reverb ( size = 1, feedback = 0.85 )

$('example')
  .randsamp('808')
  .reverb(rf())
  .play();
  // different reverb size for random sample each loop

reverse ( )

$('example')
  .ramp(0, 1, 128)
  .reverse();
  // goes from 1 to 0 over 128 values

round ( )

$('example')
  .from([0.1, 0.5, 0.9, 1.1])
  .round();
  // 0 1 1 1

saheach ( n )

$('example')
  .noise(6)
  .saheach(2);
  // 0.33173470944031735, 0.33173470944031735, 0.17466890792169742, 0.17466890792169742, 0.5601080880419886,  0.5601080880419886  

size ( new_size )

$('example')
  .noise(1000)
  .size(n1)
  .play();
  // upscaling 1000 samples of noise to be 1 whole note long

scale ( new_min, new_max, exponent = 1 )

$('example')
  .sine(10, 100)
  .scale(0, 1);
  // unipolar signal

shift ( amt )

$('example')
  .from([1, 2, 3, 4])
  .shift(-0.5);
  // 3 4 2 1

shuffle ( prob = 1 )

$('example')
  .from([1, 2, 3, 4])
  .shuffle();
  // first time: 3 2 1 4
$('example')
  .from([1, 2, 3, 4])
  .shuffle();
  // second time: 1 3 4 2

skip ( prob )

$('example')
  .spiral(16, random(1, 360))
  .skip(0.95);
  // new pattern 5% of the time when this command runs

slew ( depth = 25, up_speed = 1, down_speed = 1 )

$('example')
  .from([0, 0.5, 0.9, 0.1])
  .slew(25, 0, 1)
  // the first three numbers will jump immediately because upwards slew is 0. then it will slew from 0.9 to 0.1 over the course of the entire depth range

smooth ( )

$('example')
  .noise(64)
  .smooth();
  // less noisy

sort ( )

$('example')
  .noise(128)
  .sort();
  // ascending values originally from noise

speed ( amt )

$('example')
  .randsamp('808')
  .speed(0.2); // slow sample
$('example')
  .randsamp('808')
  .speed(1.5); // fast sample

sticky ( amt )

$('example')
  .noise(n4)
  .sticky(0.98);
  // quarter note of "sticky" noise

stretchto ( num_samples )

$('example')
  .sine(1000, n2)
  .stretchto(n1)
  .play();
  // 1000Hz sine wave originally a half note long, stretched to a whole note

stutter ( number_of_repeats )

$('example')
  .iter(8, () => {
    this.sup(_.randsamp('808')
      .stutter(8), i / iters)
  })
  .play();
  // 8 random 808 samples, each stuttered 8 times

subset ( percentage )

$('example')
  .phasor(1)
  .size(50)
  .subset(0.3);
  // originally 50 values long, now 0.02 0.08 0.50 0.58 0.62 0.700 0.76 0.78 0.92

truncate ( length )

$('example')
  .from([0, 1, 2, 3])
  .truncate(2);
  // now 2 values long
$('example')
  .from([0, 1, 2, 3])
  .truncate(6);
  // still 4 values long

tune ( note = "c", binThreshold = 0.005 )

$('example')
  .noise(n1)
  .tune('c', 0.0001)
  .play();
  // tuning a whole note of noise to c

unique ( )

$('example')
  .from([1, 2, 3, 0, 0.4, 2])
  .unique();
  // 1 2 3 0 0.4

walk ( prob, amt )

$('example')
  .from([0, 1, 2, 0, 1, 0.5, 2, 0])
  .walk(0.25, 3);

wrap ( min, max )

$('example')
  .sine(100)
  .add(-0.1)
  .wrap(0.2, 0.5)
  .play();

Pattern modulators that can take a FacetPattern, number, or array as an argument

When a modulator takes a FacetPattern or an array as an argument, it uses that pattern to dynamically change its behavior over time, affecting the output in a more complex way than if a single number were supplied. For example, with the command $('example').noise(16).add(4), all 16 output values will be between 3 and 5, because 4 is added to every noise value, and noise values are between -1 and 1 by default. But with the command $('example').noise(16).add(_.ramp(0,4,16)), the output values will ramp from between [-1, 1] at the beginning to between [4, 5] at the end, since the FacetPattern that is being added is a ramp of values starting at 0 and ending at 4.

add ( FacetPattern, match_sizes = true )

$('example')
  .randsamp('808')
  .add(_.randsamp('808'))
  .play();
  // two random samples each loop

bpf ( cutoffPattern = 1000, q = 2.5 )

$('example')
  .noise(n1)
  .bpf(1000, 6)
  .times(0.1)
  .play();
  // band-passed noise
$('example')
  .noise(n1)
  .bpf(_.sine(4)
    .scale(10, 1000))
  .play();
  // 4-cycle LFO modulating the bandpass cutoff between 10 and 1000 Hz

comb ( delaySamplesPattern = sample_rate / 100, feedforward = 0.5, feedback = 0.5 )

$('example')
  .noise(n4)
  .comb(ms(10), 0.5, 0.5)
  .play();

crush ( numberOfBitsPattern, downsamplingPattern )

$('example')
  .sine(100)
  .crush(2)
  .play();
  // redux on the sine wave
$('example')
  .sine(100, n1)
  .crush(_.ramp(8, 1, 8))
  .play();
  // ramping bit depth on 100Hz sine wave from 8 bits to 1
$('example')
  .sine(100, n1)
  .crush(_.ramp(8, 1, 8), _.noise(16)
    .scale(1, 40))
  .play();
  // ramping bit depth on 100Hz sine wave from 8 bits to 1, and dynamically changing the downsampling amount between 1 and 40 samples

delay ( delaySamplesPattern, feedback = 0.5 )

$('example')
  .randsamp('808')
  .delay(random(1700, 10000))
  .play();

divide ( FacetPattern, match_sizes = true )

$('example')
  .sine(1)
  .divide(_.from([0.5, 0.25, 0.1, 1]));

ffilter ( minFreqPattern, maxFreqPattern, invertMode = false)

$('example')
  .noise(n16)
  .ffilter(200, 2000)
  .play();
  // noise between 200Hz - 2000Hz

fgate ( gateThresholdPattern = 0.1, invertMode = false )

$('example')
  .noise(n16)
  .fgate(0.7)
  .play();
  // try experimenting with different threshold values

flookup ( lookupPattern )

$('example')
  .randsamp('808')
  .flookup(_.ramp(1, 0, SAMPLE_RATE))
  .play()
  .once();
  // backwards 808 sample, ramping from relative position 1 to 0

fshift ( shiftAmountPattern )

$('example')
  .sine(100)
  .fshift(0.04)
  .play();
  // try experimenting with different shift values

ftilt ( tiltAmountPattern )

- applies spectral harmonic tilting to the FacetPattern. This method allows for the rotation of the harmonic bands of the signal, enabling each band to be repositioned independently in time. The `tiltAmountPattern` should be normalized to values between -1 and 1, where -1 represents a full counter-clockwise rotation and 1 represents a full clockwise rotation.
$('example')
  .sample('808/808-Clap03')
  .ftilt(_.ramp(rf(), rf(), 100))
  .play();
  // split the clap sample into 100 frequency bands and disperse them randomly in time

harmonics ( numHarmonicsPattern )

$('example')
  .sine(10)
  .harmonics(200)
  .play();
  // 10Hz sine wave with 200 harmonics added on top
$('example')
  .sine(10, n1)
  .harmonics(_.ramp(0, 200, 200))
  .play();
  // ramping up from 0 harmonics on the 10Hz wave to 200 harmonics

hpf ( cutoffPattern = 100, q = 2.5 )

$('example')
  .noise(n1)
  .hpf(2000, 6)
  .times(0.1)
  .play();
  // high-passed noise
$('example')
  .noise(n1)
  .hpf(_.sine(4)
    .scale(10000, 20000))
  .play();
  // 4-cycle LFO modulating the high pass cutoff between 10000 and 20000 Hz

lpf ( cutoffPattern )

$('example')
  .noise(n1)
  .lpf(1000, 6)
  .times(0.1)
  .play();
  // low-passed noise
$('example')
  .noise(n1)
  .lpf(_.sine(4)
    .scale(10, 2000))
  .play();
  // 4-cycle LFO modulating the high pass cutoff between 10 and 2000 Hz

pitch ( pitchShiftPattern )

$('example')
  .sine(100)
  .shift(rf(0.5, 2));
  // sometimes lower pitch, sometimes higher pitch
$('example')
  .sine(100)
  .pitch(_.noise(16)
    .scale(0.5, 2))
  .play();
  // pitch shifts a 100Hz wave at 16 places, sometimes lower and sometimes higher

stretch ( shiftAmountPattern, chunksPerSecondPattern = 128 )

$('example')
  .sine(100, n4)
  .stretch(4)
  .play();
  // stretching a quarter note sine wave to last a whole note
$('example')
  .noise(n1)
  .stretch(_.ramp(0.125, 4, 16))
  .play()
  .once();
  // stretching a whole note of noise over 16 ramped values, starting at 8x faster and ending at 4x slower

subtract ( FacetPattern, match_sizes = true )

$('example')
  .sine(100)
  .subtract(_.cosine(50))
  .play();

tanh ( gainPattern = 20 )

$('example')
  .phasor(1, 20)
  .times(10)
  .tanh(6);
  // 0 0.995 0.9999 0.99999996 0.9999999999 0.999999999999 0.9999999999999996 1 1 1 1 1 1 1 1 1 1 1 1 1
$('example')
  .sine(100)
  .tanh(_.ramp(0, 100, 100))
  .play();
  // ramping tanh distortion up on a 100Hz sine wave

times ( FacetPattern, match_sizes = true)

$('example')
  .sine(50)
  .times(_.sine(50))
  .play();

Pattern modulators that must take a second FacetPattern as an argument

and ( FacetPattern, match_sizes = true )

$('example')
  .from([1, 0, 1, 0])
  .and(_.from([0, 1]));
  // 0 0 1 0

append ( FacetPattern )

$('example')
  .sine(1)
  .append(_.phasor(1))
  .append(_.from([1, 2, 3, 4]));

chaos ( FacetPattern, iterations = 100, cx = 0, cy = 0)

$('example')
  .sine(n1)
  .chaos(_.drunk(n1, 0.01))
  .play();

convolve ( FacetPattern )

$('example')
  .randsamp('808')
  .convolve(_.randsamp('808'))
  .play();
  // convolving random samples

equals ( FacetPattern, match_sizes = true )

$('example')
  .sine(1)
  .equals(_.sine(2));

ichunk ( FacetPattern )

$('example')
  .randsamp('808')
  .ichunk(_.ramp(rf(), rf(), 256))
  .play();
  // play 256 slices between two random points of a random sample... timestretching :)

interlace ( FacetPattern )

$('example')
  .sine(1)
  .interlace(_.phasor(1, 20));

map ( FacetPattern )

$('example')
  .from([1, 2, 3, 4])
  .map([11, 12, 13, 14]);
  // 11 11 11 11
$('example')
  .from([1, 2, 3, 4])
  .scale(30, 34)
  .map(_.from([31, 31.5, 32, 32.5]));
  // 31 31.5 32.5 32.5

or ( FacetPattern, match_sizes = false )

$('example')
  .from([1, 0, 1, 0])
  .or(_.from([0, 1]));
  // 1 0 1 1

sieve ( FacetPattern )

$('example')
  .noise(1024)
  .sieve(_.sine(10));
  // sieving noise with a sine wave into the audio rate :D

splice ( FacetPattern, position )

$('example')
  .randsamp('808')
  .splice(_.noise(n16), 0.5)
  .play();
  // inserts a 16th note of noise halfway through the random sample

sup ( FacetPattern, startPositionPattern, maxFrameSize = whole_note_samples )

$('example')
  .silence(n1)
  .sup(_.randsamp('808'), 0, n1)
  .sup(_.randsamp('808'), 0.5, n1)
  .play();
  // superpose two samples at the 0% and 50% points through each loop

vocode ( carrierPattern )

$('example')
  .seq('808/* 808/* 808/* 808/* 808/* 808/* 808/* 808/*')
  .vocode(_.square([220, 440, 110, 110]))
  .play();
  // vocode sequence of random 808 sample with simple square wave pattern

Pattern modulators with a function as one of the arguments

For more examples, refer to the examples/this.md file.

mix ( wet, command = function )

$('example')
  .randsamp('808')
  .mix(0.5, () => {
    this.reverse()
      .speed(2)
      .echo(8)
      .speed(10)
  })
  .play();

iter ( num_times, command = function, prob = 1 )

$('example')
  .randsamp('808')
  .iter(8, () => {
    this.delay(ri(1, 2000))
  })
  .play();
  // 8 delay lines between 1 and 2000 samples

parallel ( commands = [function, function] )

$('s')
  .noise(n4)
  .scale(-1, 1)
  .allpass(347)
  .allpass(113)
  .allpass(37)
  .parallel([() => {
    this.delay(1687, 0.975)
  }, () => {
    this.delay(1601, 0.975)
  }, () => {
    this.delay(2053, 0.975)
  }, () => {
    this.delay(2251, 0.975)
  }])
  .play()
  .full();
  // schroeder reverb on a quarter note of noise

run ( commands = function )

$('example')
  .noise(n1)
  .run(() => {
    if (bars % 2 == 0) {
      this.tune('c')
    } else {
      this.tune('g')
    }
  })
  .play();
  // alternating c and g whole notes made from tuned noise

seq ( sequencePattern, commands = function )

$('example')
  .seq('kicks/* hats/* snares/003 hats/003')
  .play();
  // random kick, random hat, snares/003, hats/003
$('example')
  .seq(_.from(['kicks/003', 'hats*', 'snares/003', 'hats/*'])
    .dup(choose([1, 3, 5, 7]))
    .palindrome()
    .rechunk(8, 0.5), () => {
      this.log(rf())
        .delay(ri(n128, n16))
    })
  .full()
  .play()
  // example using commands to proess each sample, and using a FacetPattern as the sequencePattern

slices ( num_slices, command = function, prob = 1, fade_mode = true )

$('example').noise(n1)
  .slices(32, () => {
    this.times(rf()).log(rf())
  })
  .play();
  // whole note of noise into 32 slices, each randomized amplitude and randomly warped with log()

sometimes ( prob, command = function() )

$('example')
  .tri(100)
  .times(_.ramp(1, 0, 1000)
    .expo(8))
  .truncate(n4)
  .dup(3)
  .sometimes(0.5, () => {
    this.reverse()
  })
  .play();
  // sometimes backwards 100Hz triangle wave, sometimes forwards

subrange ( min, max, command = function() )

$('example')
  .tri(200)
  .subrange(0.33, 0.66, () => {
    this.times(_.from([1,0,0,0,0,0,0,1]).curve())
  })
  .play();
  // quieter in the middle third

Methods for image generation and processing

Because Facet generates and modifies a 1-dimensional array of data, it is also possible to generate images from data. NOTE: images are expected to have equal height and width dimensions (a perfect square). Some methods will produce distorted output or throw errors if you try to run them with patterns that are not perfect squares.

circle2d ( centerX, centerY, radius, value, fillMode = 0 )

$('example')
  .silence(1000000)
  .circle2d(100, 100, 100, 1)
  .saveimg('example_circle');
  // white circle in a 1000x1000 image
$('example')
  .silence(1000000)
  .circle2d(100, 100, 100, 1, 1250, 800)
  .saveimg('circle2d', [1, 1, 1], 1250, 800)
  .once();
  // white circle in a 1250x800 image

delay2d (delayX, delayY, intensityDecay = 0.5 )

$('example')
  .silence(1000000)
  .circle2d(500, 500, 250, 1)
  .delay2d(20, 20, 0.85)
  .saveimg('delay2d')
  .once();
  // circle echoing down-left

draw2d ( coordinates, fillValue )

$('example')
  .silence(10000)
  .draw2d([0, 0, 99, 99], 1)
  .saveimg('draw2d')
  .once();
  // draw a line from (0, 0) to (99, 99) with a fill value of 1

grow2d ( iterations, prob, threshold = 0, mode = 0 )

$('example')
  .noise(1000000)
  .grow2d(5, 0.5, 0.2, 1)
  .saveimg('grow2d')
  .once();
  // apply the growth algorithm to a noise pattern

layer2d ( brightness_data, xCoords, yCoords )

$('example')
  .sine(1)
  .size(10000)
  .scale(0, 1)
  .layer2d(_.noise(10000), _.ramp(0, 100, 128), _.ramp(0, 100, 128))
  .saveimg('layer2d')
  .once();
  // layers a ramp from 0,0 to 100,100 over a sine wave background

mutechunks2d ( num_chunks, probabilty )

$('example')
  .sine(0.3, 1000)
  .scale(0, 1)
  .mutechunks2d(36, 0.5)
  .saveimg('mutechunks2d')
  .once();

palindrome2d ( )

$('example')
  .silence(1000000)
  .iter(128, () => {
    this.rect2d(ri(0, 1000), ri(0, 1000), ri(10, 100), ri(10, 100), rf())
  })
  .palindrome2d()
  .invert()
  .saveimg('palindrome2d', [_.ramp(rf(), rf(), 1000000), _.ramp(rf(), rf(), 1000000), _.ramp(rf(), rf(), 1000000)])
  .once();
  // 128 rectangles in a 2d palindrome

rechunk2d ( num_chunks )

$('example')
  .sine(0.3, 1000)
  .scale(0, 1)
  .rechunk2d(36)
  .saveimg('rechunk2d')
  .once();

rect2d ( topLeftX, topLeftY, rectWidth, rectHeight, value, fillMode = 0 )

$('example')
  .silence(1000000)
  .rect2d(0, 0, 100, 100, 1)
  .saveimg('rect2d')
  .once();
  // 100x100 white square in top-left corner of 1000x1000 image

rotate ( angle )

$('example')
  .sine(1)
  .scale(0, 1)
  .size(512 * 512)
  .rotate(35)
  .saveimg('rotate')
  .once();
  // rotates a sine wave background 35 degrees

saveimg ( filepath = Date.now(), rgbData )

$('example')
  // create black background
  .silence(512 * 512)
  // add the 512 brightest-possible pixels (1s) that will be used to create a circle
  .layer2d(_.from(1)
    .size(512),
    // the circle x coordinates move from left edge (0) to right edge (512) and back
    _.ramp(0, 511, 512)
    .palindrome(),
    // the circle y coordinates, pt. 1: create a half-circle out of 512 values, defaulting to between 0 and 1
    _.circle(1)
    .size(512)
    // the circle y coordinates, pt. 2: append another half-circle out of 512 values, scaled between -1 and 0 and inverted
    .append(_.circle(1)
      .size(512)
      .scale(-1, 0)
      .invert())
    // scale the y coordinates so they move between 0 and 511
    .scale(0, 511))
  .saveimg('circle',
    // use 3 random ramps, 1 for each RGB channel, to create a gradient in the circle's pixels
    [_.ramp(rf(), rf(), 512), _.ramp(rf(), rf(), 512), _.ramp(rf(), rf(), 512)]
  )
  .once();

savespectrogram ( filePath, windowSize = 2048 )

$('example')
  .noise(n1)
  .ffilter(_.ramp(0, NYQUIST / 2), _.ramp(NYQUIST, NYQUIST / 2))
  .savespectrogram('mytri' + Date.now())
  .once();

shift2d ( xAmt, yAmt, mode )

$('example')
  .noise(100 * 100)
  .prob(0.001)
  .iter(4, () => {
    this.mix(0.5, () => {
      this.shift2d(0, 1)
    })
  })
  .saveimg('shift2d')
  .once();
  // slides all the pixels up 4

size2d ( size )

$('example')
  .noise(10000)
  .size2d(0.5)
  .saveimg('size2d')
  .once();
  // 100 x 100 image with a square of noise in the center

slices2d ( num_slices, command )

$('example')
  .noise(1000000)
  .slices2d(36, () => {
    this.times(rf())
  })
  .saveimg('slices2d')
  .once();

spectral ( stretchFactor = 1 )

$('example')
  .silence(1000000)
  .iter(16, () => {
    this.circle2d(ri(0, 999), ri(0, 999), ri(0, 100), rf())
  })
  .spectral()
  .play()
  .full()
  .once();
  // 16 circles randomly dispersed and superposed around the audio spectrum

tri2d ( x1, y1, x2, y2, x3, y3, value, fillMode = 0 )

$('example')
  .silence(1000000)
  .tri2d(ri(0, 1000), ri(0, 1000), ri(0, 1000), ri(0, 1000), ri(0, 1000), ri(0, 1000), 1)
  .saveimg('tri2d')
  .once();
  // one random white triangle in a 1000x1000 image

warp2d ( warpX, warpY, warpIntensity )

$('example')
  .silence(1000000)
  .circle2d(100, 100, 100, 0.2)
  .delay2d(20, 20, 0.98)
  .iter(4, () => {
    this.warp2d(ri(0, 999), ri(0, 999), rf())
  })
  .saveimg('warp2d')
  .once();
  // echoing circles warped to a random position in the 2d space

walk2d ( percentage, x, y, mode = 0 )

$('example')
  .silence(1000000)
  .rect2d(950, 950, 50, 50, 1)
  .walk2d(0.5, 10, 10, 0)
  .saveimg('walk2d')
  .once();
  // white square in bottom corner, 50% random walked by 10px in all 4 directions