Public
Edited
Aug 11, 2024
Insert cell
Insert cell
smallQuiltsData
X
n
Y
numPeriodLengths
Color
pisanoPeriod
Size
Facet X
Facet Y
Mark
dot
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
smallQuiltsData = smallQuilts.map((quilt) => {
const maxRowIndex = _.max(quilt.sequences.map((x) => x[0]));
const maxColIndex = _.max(quilt.sequences.map((x) => x[1]));
const pisanoPeriod = quilt.sequences[1].length;
return {
n: quilt.n,
numCycles: quilt.sequences.length,
pisanoPeriod,
pisanoPeriodNormed: pisanoPeriod / quilt.n,
numPeriodLengths: quilt.periods().length,
maxRowIndex,
maxRowIndexNormed: maxRowIndex / quilt.n,
maxColIndex,
maxColIndexNormed: maxColIndex / quilt.n,
// whether the lucas numbers are in their own separate sequence
hasLucas: quilt.sequences.some((x) => x[0] === 1 && x[1] === 3),
mod5: quilt.n % 5,
isPrime: smallPrimesSet.has(quilt.n),
isFibo: fiboSet.has(quilt.n)
};
})
Insert cell
smallQuilts = _.range(1, SMALL_NUMBER_LIMIT + 1).map(pisanoQuilt)
Insert cell
viewof n = Inputs.number({ label: "N", value: 144 })
Insert cell
smallQuilts.filter((quilt) =>
quilt.sequences.some(
(seq) => (quilt.sequences[1].length - 1) % (seq.length - 1) !== 0
)
)
Insert cell
quilt = pisanoQuilt(n)
Insert cell
sequenceStats
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
html`${quilt.sequences.map((seq) => drawCycle(n, seq))}`
Insert cell
sequenceStats = quilt.sequences.map((seq) => ({ seq }))
Insert cell
function drawCycle(n, seq) {
let rad = 45;
let path = d3Path.path();
path.moveTo(...pathCoord(n, seq[0], rad));
for (let i = 1; i < seq.length; i++) {
path.lineTo(...pathCoord(n, seq[i], rad));
}
return svg`<svg width="100" height="100" viewbox="-50 -50 100 100">
<path d="${path.toString()}" fill="none" stroke="black" stroke-width="0.5"></path>
</svg>`;
}
Insert cell
Insert cell
function takeWhile(f, pred) {
const result = [];
for (const item of f) {
if (pred(item)) {
result.push(item);
} else {
return result;
}
}
}
Insert cell
SMALL_NUMBER_LIMIT = 1024
Insert cell
fiboSet = new Set(smallFibo)
Insert cell
smallFibo = takeWhile(fibogen(), (n) => n <= SMALL_NUMBER_LIMIT)
Insert cell
function* fibogen() {
let i = 1;
let j = 1;
while (true) {
yield j;
let k = j;
j = j + i;
i = k;
}
}
Insert cell
smallPrimesSet = new Set(smallPrimes)
Insert cell
smallPrimes = takeWhile(primes(), (p) => p <= SMALL_NUMBER_LIMIT)
Insert cell
// playCycle(n, quilt.sequences[1])
Insert cell
async function playCycle(n, cycle) {
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let pitches = [...Array(n)].map((_, k) => {
return k / n;
});
for (let k of cycle) {
await playShepard(audioCtx, pitches[k], 500);
}
}
Insert cell
// tone goes from 0 (A) to 1
function playShepard(audioCtx, tone, duration) {
const pitches = [tone - 2, tone - 1, tone, tone + 1, tone + 2].map((p) => {
return {
freq: 261.626 * (1 + p),
gain: getShepardGain(p) * 0.5
};
});
return Promise.all(
pitches.map((pitch) => playNote(audioCtx, pitch.freq, duration, pitch.gain))
);
}
Insert cell
function getShepardGain(tone) {
if (tone <= -2 || tone >= 2) {
return 0;
}
return (1 + Math.cos((Math.PI / 2) * tone)) / 2;
}
Insert cell
{
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// playNote(audioCtx, 440, 1000);
// await playNote(audioCtx, 440 * 1, 1000);
// await playNote(audioCtx, 440 * (1 + 1/2), 1000);

// playNote(audioCtx, 440 * (1 + 1 / 4), 1000);
// playNote(audioCtx, 440 * (1 + 1 / 2), 1000);
}
Insert cell
async function playNote(audioCtx, freq, duration, gain = 0.5) {
const oscillator = audioCtx.createOscillator();
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(freq, audioCtx.currentTime); // value in hertz
var gainNode = audioCtx.createGain();
gainNode.gain.value = 0.1;
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.exponentialRampToValueAtTime(gain, audioCtx.currentTime + 0.1);
oscillator.start();
await timeout(duration - 10);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.1);
await timeout(10);
oscillator.stop();
}
Insert cell
function timeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Insert cell
import { primes } from "@mourner/fast-prime-generator"
Insert cell
import { pisanoQuilt } from "@tesseralis/pisano-quilts"
Insert cell
d3Path = require("d3-path")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more