More generative methods, but in general more complex algorithms, such as euclidean rhythm generation, lindenmayer string expansion, fibonacci number sequence, pisano periods and more.
const Algo = require('total-serialism').Algorithmic;
const { euclid, fibonacci } = require('total-serialism').Algorithmic;
- euclidean
- fastEuclidean
- hexBeat
- linden
- cellular automaton
- collatz
- fibonacci
- pisano
- pell
- threeFibonacci
- lucas
- nbonacci
- inifinity series
I recommend using fastEuclid()
instead
Generate a euclidean rhythm evenly spacing n-hits amongst n-steps. Inspired by Godfried Toussaints famous paper "The Euclidean Algorithm Generates Traditional Musical Rhythms".
Alias: euclid()
arguments
- {Int+/Array} -> length of array (optional, default=8, uses length of Array if input)
- {Int+/Array} -> hits (optional, default=4, uses length of Array if input)
- {Int} -> rotate (optional, default=0)
Algo.euclid(8, 5);
//=> [ 1, 0, 1, 1, 0, 1, 1, 0 ]
Algo.euclid(16, 9, 1);
//=> [ 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1 ]
A fast euclidean rhythm generating algorithm. Uses the downsampling of a line drawn between two points in a 2-dimensional grid to divide the squares into an evenly distributed amount of steps. Generates the correct distribution, but the rotation/order may differ a bit from the recursive euclid()
method above.
Alias: fastEuclid()
arguments
- {Int+/Array} -> length of array (optional, default=8, uses length of Array if input)
- {Int+/Array} -> hits (optional, default=4, uses length of Array if input)
- {Int} -> rotate (optional, default=0)
Algo.fastEuclid(8, 5);
//=> [ 1, 0, 1, 0, 1, 1, 0, 1 ]
Algo.fastEuclid(16, 9, 1);
//=> [ 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0 ]
Generate hexadecimal rhythms. Hexadecimal beats make use of hexadecimal values (0 - f) that are a base-16 number system. Because one digit in a base-16 number system has 16 possible values (0 - 15) these can be converted to 4 bits that therefore can be seen as groups of 4 16th notes. These hexadecimal values will then represent any permutation of 1's and 0's in a 4 bit number, where 0 = 0 0 0 0, 7 = 0 1 1 1, b = 1 0 1 1, f = 1 1 1 1 and all possible values in between. This method does not work with hexadecimal notation (0x...
), for that use the binary()
as an alternative.
Alias: hex()
arguments
- {String | Number} -> hexadecimal characters (0-f) (optional, default=8)
// generate a hexadecimal rhythm based on a hexadecimal string (0-f)
// inspired by Steven Yi's implementation in CSound Live Coding
Algo.hexBeat('a9d2');
//=> [ 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0 ]
Algo.hexBeat(573);
//=> [ 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1 ]
The original Lindenmayer string expansion returns a string of characters based on a set of rules and an axiom specified as strings. This is useful to generate fractal like structures and simulate natural growth.
arguments
- {Value} -> The axiom to start with (generation 0)
- {Int+} -> The number of generations to iterate
- {Object} -> The object or reference to object with rules
// Koch curve
Algo.linden('F', 2, {F: 'F+F-F-F+F'});
//=> 'F+F-F-F+F+F+F-F-F+F-F+F-F-F+F-F+F-F-F+F+F+F-F-F+F'
// Cantor set
Algo.linden('A', 3, {A: 'ABA', B: 'BBB'});
//=> 'ABABBBABABBBBBBBBBABABBBABA'
// Sierpinski Triangle
Algo.linden('F-G-G', 1, {'F': 'F−G+F+G−F', 'G' : 'GG'});
//=> 'F−G+F+G−F-GG-GG'
A more useful version that works nicely with the rest of the library. By returning an array of integers it can be quickly put to use in combination with other methods to generate rhythms, melodies and more based on custom rulesets.
Algo.linden();
//=> [ 1, 0, 1, 1, 0 ] (default)
// Cantor set as 0's and 1's in an array ruleset
Algo.linden(1, 3, {1: [1, 0, 1], 0: [0, 0, 0]});
//=> [ 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1 ]
// Set more complex rules for generating semitones for example
var complexRules = {
0: [0, 3, 7],
3: [-1, 0],
7: [12, 19, 0],
12: [12, 0, 0, 5],
5: [0, -3, 0]
}
Algo.linden(0, 2, complexRules);
//=> [ 0, 3, 7, -1, 0, 12, 19, 0, -1, 0, 3, 7, 12, 0, 0, 5, 19, 0, 3, 7 ]
Generate an Elementary Cellular Automaton class (1D). This is a one-dimensional array (collection of cells) with states that are either dead or alive (0/1). By following a set of rules the next generation is calculated for every cell based on its neighbouring cells. Invoke the next()
method to iterate the generations. Set the first generation with the feed()
method (usually random values work quite well). Change the rule()
based on a decimal number or an array of digits.
methods
- @constructor {length, rule} -> generate the CA
- @get state -> return the current generations as array
- @get table -> return the table of rules
- @method rule() -> set the rule based on decimal number or array
- @method feed() -> feed the initial generation with an array
- @method next() -> generate the next generation and return
let ca = new Algo.Automaton();
// feed with 40 randomly generated values 0-1
ca.feed(Rand.coin(40));
// set the rule with a decimal representation
ca.rule(122);
// generate the next generation and store in array
let gen = ca.next();
// create multiple generations in a forloop
let gens = [];
for (let i=0; i<10; i++){
gens.push(ca.next());
}
Util.draw(gens);
// ███ ██ █ █ ██ █ █████ ██ ████ ██
// ██ █████ █ █ ████ ██ ██ █████ █████
// ████ ██ █ ██ █████ █████ ████ ███
// ██ ████ ██████ ███ ██ ██ ██ ██
// █████ ███ ██ ██ ██ ██████████████
// ██ ████ ██ ███████████ ██
// ████ ██ ███████ ██ ███
// ███████ ██ ████ ██
// ██ ██ ████ ██ ██ ████
// ████ ████ ██ ██ ████████ ██ ██
Different rules hold different patterns:
ca.rule(120);
// ██ ████ ████ █ ███ █ █ ██ █ ██
// ████ █ ███ ██ █ █ ██ █ █ ███ ███
// ██ █ █ ██ ███ █ ████ █ ██ ██ █
// ███ █ █████ ██ ██ ██ █ ██████ █
// █ ██ ██ ████████ ███ █ █ ██ █
// █ ███████ █ ███ ██ █ █ ███
// █ █ ██ █ █ █████ █ █ █ ██
// █ █ ███ █ ██ ██ █ █ ████
// █ █ █ ██ █ ███ ███ █ █ █ ██
// █ █ █ ████ █ █ ██ █ ██ █ █ █ ██
ca.rule(9);
// █ ████ █ █ █ █
// ██ ██████████ █ ███████ █
// █ █ ███████ █ ████ █
// █ ██████████ █ █████ █
// ███ █ ███████ █ ████
// █ █ ██████████ █ █████ █
// ███ █ ███████ █ ██
// ███ █ ██████████ █ █████ █
// █ ███ █ ███████ █
// ███ █ ██████████ █ ██████
Generate an array of numbers from the Collatz Conjecture, also known as the 3n+1
conjecture. Start with any positive integer n
. Each next number is obtained from the previous number as follows: If the previous number is even then the next term is the previous term divided by 2. If the previous term is odd tthen the next term is 3 times the prevous term plus 1. The conjecture is that no matter what value of n
, the sequence will always reach one. The length of the output is quite unpredicatable and can therefore be an interesting sequence for algorithmic composition.
arguments
- {Int+} -> starting point
- {Int+} -> modulus (optional, for
collatzMod
, default=2)
// the collatz sequence for the number 15
Algo.collatz(7);
//=> [
// 1, 2, 4, 8, 16, 5,
// 10, 20, 40, 13, 26, 52,
// 17, 34, 11, 22
// ]
// return the collatz sequence with a modulus operation (default = 2)
Algo.collatzMod(7, 12);
//=> [
// 1, 2, 4, 8, 4, 5,
// 10, 8, 4, 1, 2, 4,
// 5, 10, 11, 10
// ]
// the collatz sequence can encounter quite big values
// so alternatively you can use bigCollatz and bigCollatzMod
// to allow for larger number calculations
Algo.bigCollatz('931386509544713451').length;
// => 2283
Algo.bigCollatzMod('931386509544713451');
Generate an array of Fibonacci numbers F[n] = F[n-1] + F[n-2]
. Numbers are by default represented as Strings in order to allow for bigger numbers than 64-bit integers can represent. The calculations are done using the bignumber.js library. A second argument sets an offset to pick a certain number from the sequence.
OEIS: A000045
(Online Encyclopedia of Integer Sequences)
arguments
- {Int+} -> output length of ring
- {Int+} -> offset, start the sequence at nth-fibonacci number (optional, default=0)
- {Bool} -> numbers as strings (optional, default=false)
// 10 fibonacci numbers, starting from 0, 1, 1 etc...
Algo.fibonacci(12);
//=> [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ]
// 2 fibonacci numbers, starting from the 100th value
Algo.fibonacci(2, 100, true);
//=> [ '354224848179261915075', '573147844013817084101' ]
Generate Pisano periods for the Fibonacci sequence. The pisano period is a result of applying a modulo operation on the Fibonacci sequence F[n] = (F[n-1] + F[n-2]) mod a
. The length of the period differs per modulus value, but the sequence will always have a repetition.
arguments
- {Int+} -> modulus for pisano period (optional, default=12)
- {Int+} -> output length of array (optional, defaults to pisano-period length)
// the pisano period for mod 7 has a length of 16
Algo.pisano(7);
//=> [ 0, 1, 1, 2, 3, 5, 1, 6, 0, 6, 6, 5, 4, 2, 6, 1 ]
// second argument gives a fixed length output
Algo.pisano(4, 10);
//=> [ 0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1 ]
Other integer sequences based on Fibonacci are also available
Algo.pell(10);
//=> [ 0, 1, 2, 5, 12, 29, 70, 169, 408, 985 ]
Algo.threeFibonacci(10);
//=> [ 0, 1, 3, 10, 33, 109, 360, 1189, 3927, 12970 ]
Algo.lucas(10);
//=> [ 2, 1, 3, 4, 7, 11, 18, 29, 47, 76 ]
Set a custom starting pair of numbers to generate an n-bonacci sequence according to the following method: F[n] = t * F[n-1] + F[n-2]
// start with 1, 3, then multiply [n-1] by 2 before adding with [n-2]
Algo.nbonacci(10, 1, 3, 2);
//=> [ 1, 3, 7, 17, 41, 99, 239, 577, 1393, 3363 ]
// this is the same as Algo.fibonacci(12)
Algo.nbonacci(12, 0, 1, 1);
//=> [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ]
Nørgård's music often features the use of the infinity series for serializing melody, harmony, and rhythm in musical composition. The method takes its name from the endlessly self-similar nature of the resulting musical material, comparable to fractal geometry. Mathematically, the infinity series is an integer sequence. "Invented in an attempt to unify in a perfect way repetition and variation," the first few terms of its simplest form are 0, 1, −1, 2, 1, 0, −2, 3, ….
OEIS: A004718
(Online Encyclopedia of Integer Sequences)
Alias: infSeries()
arguments
- {Int+/Array} -> output length of the Meldoy's steps (default=16, uses length of Array if input)
- {Array} -> seed the sequence's first two steps (defaults = [0, 1])
- {Int} -> offset from which the sequence starts
Algo.infinitySeries();
//=> [
// 0, 1, -1, 2, 1, 0,
// -2, 3, -1, 2, 0, 1,
// 2, -1, -3, 4
// ]
Algo.infinitySeries(16, [0, 3]);
//=> [
// 0, 3, -3, 6, 3, 0,
// -6, 9, -3, 6, 0, 3,
// 6, -3, -9, 12
// ]
Algo.infSeries(8, [0, 1], 120);
//=> [
// -4, 5, 3, -2,
// 5, -4, -6, 7
// ]