Compare commits

...

41 Commits

Author SHA1 Message Date
Felix Roos 88c4e5913f add new web audio only tune to starter 2022-06-18 17:42:01 +02:00
Felix Roos 3743088521 Merge branch 'webaudio-rewrite' into starter-template 2022-06-18 17:39:43 +02:00
Felix Roos c3a3a68054 add snapshot 2022-06-18 17:27:57 +02:00
Felix Roos 925fb30e05 fix cracks + add tune 2022-06-18 17:25:36 +02:00
Felix Roos 71b6b83ab3 samples: support speed, begin and end 2022-06-18 13:56:41 +02:00
Felix Roos d532f8007e move sampler to webaudio
+ add basic sample playback to .out
2022-06-18 13:11:25 +02:00
Felix Roos 44574af3eb use closures to simplify api 2022-06-18 00:56:58 +02:00
Felix Roos faa4019b27 dont use tone audio context 2022-06-18 00:54:36 +02:00
Felix Roos 2157675eb3 update format for new .out 2022-06-18 00:52:35 +02:00
Felix Roos 11f175a681 dont use tone audio context 2022-06-18 00:52:09 +02:00
Felix Roos 279bdc7559 Merge branch 'webaudio-rewrite' into starter-template 2022-06-18 00:44:45 +02:00
Felix Roos c1e934d171 regenerate snapshots - update webaudio ones 2022-06-18 00:39:25 +02:00
Felix Roos 5fd88f9213 fix snapshots for objects 2022-06-18 00:38:51 +02:00
Felix Roos 2bb095d47d add controls to tune test 2022-06-17 23:40:38 +02:00
Felix Roos a4a30e8b60 add new tune 2022-06-17 23:26:07 +02:00
Felix Roos 4c05876066 rewrite webaudio + migrate tunes + empty setters 2022-06-17 23:23:51 +02:00
Felix Roos f69d776cf6 prepare webaudio rewrite 2022-06-16 23:53:29 +02:00
Felix Roos 25795eaecc Merge pull request #136 from tidalcycles/on-trigger
add onTrigger helper
2022-06-16 21:58:37 +02:00
Felix Roos 3e1e92a6f1 add onTrigger helper 2022-06-16 21:01:35 +02:00
Felix Roos f2f3511a2f Merge pull request #134 from tidalcycles/scheduler-improvements
Scheduler improvements
2022-06-16 20:48:23 +02:00
Felix Roos 0ce70f4d0d Merge pull request #135 from tidalcycles/trigger-simplification
remove cycle + delta from onTrigger
2022-06-16 20:48:06 +02:00
Felix Roos cac5d96c16 remove cycle + delta from onTrigger 2022-06-16 20:38:21 +02:00
Felix Roos 25d6ff1c90 can now set latency on the scheduler 2022-06-16 20:37:13 +02:00
Felix Roos 87de8c0800 automatically run onTrigger in scheduler 2022-06-16 20:36:59 +02:00
Felix Roos 652ced944c set latency on scheduler 2022-06-16 20:36:17 +02:00
Felix Roos 0e33936942 can now set latency on the scheduler 2022-06-16 20:36:11 +02:00
Felix Roos c9f6f421d1 Merge branch 'starter-template' of https://github.com/tidalcycles/strudel into starter-template 2022-06-16 18:55:19 +02:00
Felix Roos b831342edc add webdirt + osc 2022-06-16 18:54:45 +02:00
Felix Roos 5b966715bb automatically run onTrigger in scheduler 2022-06-16 18:54:27 +02:00
Felix Roos 557f6ca3f8 dont pass cycle + delta 2022-06-16 18:54:14 +02:00
Felix Roos 01b1fc0347 calculate cycle + delta in osc 2022-06-16 18:54:03 +02:00
Felix Roos 6cd19526b0 remove strudel packages from deps to get symlinks 2022-06-16 18:53:48 +02:00
Felix Roos 8c2051a67c Update README.md
add run instr
2022-06-16 16:04:42 +02:00
Felix Roos c785c64599 add strudel starter template 2022-06-16 16:02:43 +02:00
Felix Roos badc4bcc2f Update README.md
remove unused import
2022-06-16 14:11:25 +02:00
Felix Roos 729b4e18ab Update README.md
replace extend with evalScope
2022-06-16 14:10:29 +02:00
alex c3f03f50b3 package locks 2022-06-16 10:30:02 +01:00
alex 8fd4cfe0a3 Merge branch 'main' of github.com:yaxu/strudel into main 2022-06-16 10:17:54 +01:00
alex 8dee07082d add tutorial to setup 2022-06-16 10:17:47 +01:00
Alex McLean 11e33f5c70 Update README.md 2022-06-16 10:10:13 +01:00
Alex McLean 3d68b0c566 Update README.md 2022-06-16 10:10:01 +01:00
34 changed files with 5294 additions and 132 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
[![Strudel test status](https://github.com/tidalcycles/strudel/actions/workflows/test.yml/badge.svg)](https://github.com/tidalcycles/strudel/actions)
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This is unstable software, please tread carefully.
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is slowly stabilising, but please continue to tread carefully.
- Try it here: <https://strudel.tidalcycles.org/>
- Tutorial: <https://strudel.tidalcycles.org/tutorial/>
+1 -1
View File
@@ -6,7 +6,7 @@
"scripts": {
"test": "npm run test --workspaces --if-present && cd repl && npm run test",
"bootstrap": "lerna bootstrap",
"setup": "npm i && npm run bootstrap && cd repl && npm i",
"setup": "npm i && npm run bootstrap && cd repl && npm i && cd ../tutorial && npm i",
"repl": "cd repl && npm run dev",
"osc": "cd packages/osc && npm run server",
"build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build",
+6 -3
View File
@@ -750,19 +750,22 @@ const generic_params = [
const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x }));
const _setter = (func) =>
const _setter = (func, name) =>
function (...pats) {
if (!pats.length) {
return this.fmap((value) => ({ [name]: value }));
}
return this.set(func(...pats));
};
generic_params.forEach(([type, name, description]) => {
controls[name] = (...pats) => _name(name, ...pats);
Pattern.prototype[name] = _setter(controls[name]);
Pattern.prototype[name] = _setter(controls[name], name);
});
// create custom param
controls.createParam = (name) => {
Pattern.prototype[name] = _setter(controls[name]);
Pattern.prototype[name] = _setter(controls[name], name);
return (...pats) => _name(name, ...pats);
};
+3 -5
View File
@@ -86,11 +86,9 @@ export class Hap {
}
showWhole() {
return `${this.whole == undefined ? '~' : this.whole.show()}: ${this.value}`;
}
showWhole() {
return `${this.whole == undefined ? '~' : this.whole.show()}: ${this.value}`;
return `${this.whole == undefined ? '~' : this.whole.show()}: ${
typeof this.value === 'object' ? JSON.stringify(this.value) : this.value
}`;
}
combineContext(b) {
+2 -2
View File
@@ -6,8 +6,8 @@
"packages": {
"": {
"name": "@strudel.cycles/core",
"version": "0.0.3",
"license": "GPL-3.0-or-later",
"version": "0.1.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"bjork": "^0.0.1",
"fraction.js": "^4.2.0"
+3
View File
@@ -1049,6 +1049,9 @@ export class Pattern {
.unit('c')
.slow(factor);
}
onTrigger(onTrigger) {
return this._withHap((hap) => hap.setContext({ ...hap.context, onTrigger }));
}
}
// TODO - adopt value.mjs fully..
+4 -2
View File
@@ -15,9 +15,11 @@ npm i @strudel.cycles/eval --save
```js
import { evaluate, extend } from '@strudel.cycles/eval';
import * as strudel from '@strudel.cycles/core';
extend(strudel); // add strudel to eval scope
evalScope(
import('@strudel.cycles/core'),
// import other strudel packages here
); // add strudel to eval scope
async function run(code) {
const { pattern } = await evaluate(code);
+2 -2
View File
@@ -6,8 +6,8 @@
"packages": {
"": {
"name": "@strudel.cycles/eval",
"version": "0.0.3",
"license": "GPL-3.0-or-later",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"estraverse": "^5.3.0",
"shift-ast": "^6.1.0",
+2 -2
View File
@@ -6,8 +6,8 @@
"packages": {
"": {
"name": "@strudel.cycles/midi",
"version": "0.0.4",
"license": "GPL-3.0-or-later",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"tone": "^14.7.77",
"webmidi": "^2.5.2"
+4 -2
View File
@@ -22,13 +22,15 @@ let startedAt = -1;
*/
Pattern.prototype.osc = function () {
return this._withHap((hap) => {
const onTrigger = (time, hap, currentTime, cps, cycle, delta) => {
const onTrigger = (time, hap, currentTime, cps) => {
const cycle = hap.wholeOrPart().begin.valueOf();
const delta = hap.duration.valueOf();
// time should be audio time of onset
// currentTime should be current time of audio context (slightly before time)
if (startedAt < 0) {
startedAt = Date.now() - currentTime * 1000;
}
const controls = Object.assign({}, { cps: cps, cycle: cycle, delta: delta }, hap.value);
const controls = Object.assign({}, { cps, cycle, delta }, hap.value);
const keyvals = Object.entries(controls).flat();
const ts = Math.floor(startedAt + (time + latency) * 1000);
const message = new OSC.Message('/dirt/play', ...keyvals);
+2 -2
View File
@@ -6,8 +6,8 @@
"packages": {
"": {
"name": "@strudel.cycles/osc",
"version": "0.0.1",
"license": "GPL-3.0-or-later",
"version": "0.1.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"osc-js": "^2.3.2"
}
+3984
View File
File diff suppressed because it is too large Load Diff
+1 -8
View File
@@ -57,14 +57,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
/* console.warn('no instrument chosen', event);
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
} else {
onTrigger(
time,
event,
currentTime,
1 /* cps */,
event.wholeOrPart().begin.valueOf(),
event.duration.valueOf(),
);
onTrigger(time, event, currentTime, 1 /* cps */);
}
} catch (err) {
console.warn(err);
+2 -2
View File
@@ -6,8 +6,8 @@
"packages": {
"": {
"name": "@strudel.cycles/tonal",
"version": "0.0.3",
"license": "GPL-3.0-or-later",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@tonaljs/tonal": "^4.6.5",
"webmidi": "^3.0.15"
+2 -2
View File
@@ -6,8 +6,8 @@
"packages": {
"": {
"name": "@strudel.cycles/tone",
"version": "0.0.4",
"license": "GPL-3.0-or-later",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@tonejs/piano": "^0.2.1",
"chord-voicings": "^0.0.1",
+1
View File
@@ -7,3 +7,4 @@ This program is free software: you can redistribute it and/or modify it under th
export * from './clockworker.mjs';
export * from './scheduler.mjs';
export * from './webaudio.mjs';
export * from './sampler.mjs';
@@ -1,6 +1,8 @@
const bufferCache = {}; // string: Promise<ArrayBuffer>
const loadCache = {}; // string: Promise<ArrayBuffer>
export const getCachedBuffer = (url) => bufferCache[url];
export const loadBuffer = (url, ac) => {
if (!loadCache[url]) {
loadCache[url] = fetch(url)
+6 -4
View File
@@ -10,18 +10,20 @@ import { State, TimeSpan } from '@strudel.cycles/core';
export class Scheduler {
worker;
pattern;
constructor({ audioContext, interval = 0.2, onEvent }) {
constructor({ audioContext, interval = 0.2, onEvent, latency = 0.2 }) {
this.worker = new ClockWorker(
audioContext,
(begin, end) => {
this.pattern.query(new State(new TimeSpan(begin, end))).forEach((e) => {
this.pattern.query(new State(new TimeSpan(begin + latency, end + latency))).forEach((e) => {
if (!e.part.begin.equals(e.whole.begin)) {
return;
}
if (e.context.onTrigger) {
// TODO: kill first param, as it's contained in e
e.context.onTrigger(e.whole.begin, e, audioContext.currentTime, 1 /* cps */);
}
if (onEvent) {
onEvent?.(e);
} else {
console.warn('unplayable event: no audio node nor onEvent callback', e);
}
});
},
+121 -62
View File
@@ -6,8 +6,11 @@ This program is free software: you can redistribute it and/or modify it under th
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
import * as strudel from '@strudel.cycles/core';
import { Tone } from '@strudel.cycles/tone';
const { Pattern, getFrequency, patternify2 } = strudel;
import { fromMidi } from '@strudel.cycles/core';
import { loadBuffer } from './sampler.mjs';
const { Pattern } = strudel;
// export const getAudioContext = () => Tone.getContext().rawContext;
let audioContext;
export const getAudioContext = () => {
@@ -17,9 +20,15 @@ export const getAudioContext = () => {
return audioContext;
};
const lookahead = 0.2;
const getFilter = (type, frequency, Q) => {
const filter = getAudioContext().createBiquadFilter();
filter.type = type;
filter.frequency.value = frequency;
filter.Q.value = Q;
return filter;
};
const adsr = (attack, decay, sustain, release, velocity, begin, end) => {
const getADSR = (attack, decay, sustain, release, velocity, begin, end) => {
const gainNode = getAudioContext().createGain();
gainNode.gain.setValueAtTime(0, begin);
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
@@ -30,65 +39,115 @@ const adsr = (attack, decay, sustain, release, velocity, begin, end) => {
return gainNode;
};
Pattern.prototype.withAudioNode = function (createAudioNode) {
return this._withHap((hap) => {
return hap.setContext({
...hap.context,
createAudioNode: (t, e) => createAudioNode(t, e, hap.context.createAudioNode?.(t, hap)),
});
});
};
Pattern.prototype._wave = function (type) {
return this.withAudioNode((t, e) => {
const osc = getAudioContext().createOscillator();
osc.type = type;
const f = getFrequency(e);
osc.frequency.value = f; // expects frequency..
const begin = t ?? e.whole.begin.valueOf() + lookahead;
const end = begin + e.duration.valueOf();
osc.start(begin);
osc.stop(end); // release?
return osc;
});
};
Pattern.prototype.adsr = function (a = 0.01, d = 0.05, s = 1, r = 0.01) {
return this.withAudioNode((t, e, node) => {
const velocity = e.context?.velocity || 1;
const begin = t ?? e.whole.begin.valueOf() + lookahead;
const end = begin + e.duration.valueOf() + lookahead;
const envelope = adsr(a, d, s, r, velocity, begin, end);
node?.connect(envelope);
return envelope;
});
};
Pattern.prototype._filter = function (type = 'lowpass', frequency = 1000) {
return this.withAudioNode((t, e, node) => {
const filter = getAudioContext().createBiquadFilter();
filter.type = type;
filter.frequency.value = frequency;
node?.connect(filter);
return filter;
});
};
Pattern.prototype.filter = function (type, frequency) {
return patternify2(Pattern.prototype._filter)(reify(type), reify(frequency), this);
};
Pattern.prototype.out = function () {
const master = getAudioContext().createGain();
master.gain.value = 0.1;
master.connect(getAudioContext().destination);
return this.withAudioNode((t, e, node) => {
if (!node) {
console.warn('out: no source! call .osc() first');
return this.onTrigger(async (t, hap, ct) => {
const ac = getAudioContext();
// calculate correct time (tone.js workaround)
t = ac.currentTime + t - ct;
// destructure value
let {
freq,
s,
n = 0,
gain = 1,
cutoff,
resonance = 1,
hcutoff,
hresonance = 1,
bandf,
bandq = 1,
pan,
attack = 0.001,
decay = 0,
sustain = 1,
release = 0.001,
speed = 1, // sample playback speed
begin = 0,
end = 1,
} = hap.value;
// the chain will hold all audio nodes that connect to each other
const chain = [];
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
// get frequency
if (!freq && typeof n === 'number') {
freq = fromMidi(n); // + 48);
}
if (!freq && typeof n === 'string') {
freq = fromMidi(toMidi(n));
}
// make oscillator
const o = ac.createOscillator();
o.type = s || 'triangle';
o.frequency.value = Number(freq);
o.start(t);
o.stop(t + hap.duration + release);
chain.push(o);
// level down oscillators as they are really loud compared to samples i've tested
const g = ac.createGain();
g.gain.value = 0.5;
chain.push(g);
// TODO: make adsr work with samples without pops
// envelope
const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hap.duration);
chain.push(adsr);
} else {
// load sample
const samples = getLoadedSamples();
if (!samples) {
console.warn('no samples loaded');
return;
}
const bank = samples?.[s];
if (!bank) {
console.warn('sample not found:', s, 'try one of ' + Object.keys(samples));
return;
} else {
if (speed === 0) {
// no playback
return;
}
if (!s) {
console.warn('no sample specified');
return;
}
const bank = samples[s];
const sampleUrl = bank[n % bank.length];
let buffer = await loadBuffer(sampleUrl, ac);
if (ac.currentTime > t) {
console.warn('sample still loading:', s, n);
return;
}
const src = ac.createBufferSource();
src.buffer = buffer;
src.playbackRate.value = Math.abs(speed);
// TODO: nudge, unit, cut, loop
let duration = src.buffer.duration;
const offset = begin * duration;
duration = ((end - begin) * duration) / Math.abs(speed);
src.start(t, offset, duration);
src.stop(t + duration);
chain.push(src);
}
}
node?.connect(master);
})._withHap((hap) => {
const onTrigger = (time, e) => e.context?.createAudioNode?.(time, e);
return hap.setContext({ ...hap.context, onTrigger });
// filters
cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance));
hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance));
bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq));
// TODO vowel
// TODO delay / delaytime / delayfeedback
// panning
if (pan !== undefined) {
const panner = ac.createStereoPanner();
panner.pan.value = 2 * pan - 1;
chain.push(panner);
}
// master out
const master = ac.createGain();
master.gain.value = 0.8 * gain;
chain.push(master);
chain.push(ac.destination);
// connect chain elements together
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
});
};
Pattern.prototype.define('wave', (type, pat) => pat.wave(type), { patternified: true });
-1
View File
@@ -1,2 +1 @@
export * from './webdirt.mjs';
export * from './sampler.mjs';
+28
View File
@@ -0,0 +1,28 @@
{
"name": "@strudel.cycles/webdirt",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/webdirt",
"version": "0.1.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"WebDirt": "github:dktr0/WebDirt"
}
},
"node_modules/WebDirt": {
"name": "webdirt",
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/dktr0/WebDirt.git#425dc8fd023440d9c61ffdb8642e44e2710faea0",
"license": "ISC"
}
},
"dependencies": {
"WebDirt": {
"version": "git+ssh://git@github.com/dktr0/WebDirt.git#425dc8fd023440d9c61ffdb8642e44e2710faea0",
"from": "WebDirt@github:dktr0/WebDirt"
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import * as strudel from '@strudel.cycles/core';
const { Pattern } = strudel;
import * as WebDirt from 'WebDirt';
import { getLoadedSamples, loadBuffer, getLoadedBuffer } from './sampler.mjs';
import { getLoadedSamples, loadBuffer, getLoadedBuffer } from '@strudel.cycles/webaudio';
let webDirt;
+2
View File
@@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
oldtunes.mjs
+2 -1
View File
@@ -13,7 +13,8 @@ import './App.css';
import logo from './logo.svg';
import * as tunes from './tunes.mjs';
import * as WebDirt from 'WebDirt';
import { loadWebDirt, resetLoadedSamples } from '@strudel.cycles/webdirt';
import { loadWebDirt } from '@strudel.cycles/webdirt';
import { resetLoadedSamples } from '@strudel.cycles/webaudio';
evalScope(
Tone,
+9 -1
View File
@@ -6,6 +6,8 @@
import { evaluate } from '@strudel.cycles/eval';
import { extend } from '@strudel.cycles/eval';
import * as strudel from '@strudel.cycles/core';
import * as webaudio from '@strudel.cycles/webaudio';
import controls from '@strudel.cycles/core/controls.mjs';
// import gist from '@strudel.cycles/core/gist.js';
import { mini } from '@strudel.cycles/mini/mini.mjs';
// import { Tone } from '@strudel.cycles/tone';
@@ -83,6 +85,9 @@ const toneHelpersMocked = {
strudel.Pattern.prototype.tone = function () {
return this;
};
strudel.Pattern.prototype.webdirt = function () {
return this;
};
// draw mock
strudel.Pattern.prototype.pianoroll = function () {
@@ -116,6 +121,7 @@ const uiHelpersMocked = {
backgroundImage: id,
};
// TODO: refactor to evalScope
extend(
// Tone,
@@ -123,6 +129,8 @@ extend(
strudel.Pattern.prototype.bootstrap(),
toneHelpersMocked,
uiHelpersMocked,
controls,
webaudio,
/* controls,
toneHelpers,
voicingHelpers,
@@ -181,6 +189,6 @@ export const testCycles = {
randomBells: 24,
waa: 16,
waar: 16,
hyperpop: 60,
hyperpop: 10,
festivalOfFingers3: 16,
};
+103 -24
View File
@@ -728,7 +728,7 @@ bell = bell.chain(vol(0.6).connect(delay),out());
.slow(6)
.pianoroll({minMidi:20,maxMidi:120,background:'transparent'})`;
export const waa = `"a4 [a3 c3] a3 c3"
/* export const waa = `n("a4 [a3 c3] a3 c3")
.sub("<7 12>/2")
.off(1/8, add("12"))
.off(1/4, add("7"))
@@ -736,23 +736,37 @@ export const waa = `"a4 [a3 c3] a3 c3"
.slow(2)
.wave("sawtooth square")
.filter('lowpass', "<2000 1000 500>")
.out()`;
.out()`; */
export const waar = `"a4 [a3 c3] a3 c3".color('#F9D649')
.sub("<7 12 5 12>".slow(2))
.off(1/4,x=>x.add(7).color("#FFFFFF #0C3AA1 #C63928"))
.off(1/8,x=>x.add(12).color('#215CB6'))
.slow(2)
.legato(sine.range(0.3, 2).slow(28))
.wave("sawtooth square".fast(2))
.filter('lowpass', cosine.range(500,4000).slow(16))
.out()
.pianoroll({minMidi:20,maxMidi:120,background:'#202124'})`;
export const waa = `n(
"a4 [a3 c3] a3 c3"
.sub("<7 12>/2")
.off(1/8, add("12"))
.off(1/4, add("7"))
)
.legato(.5)
.slow(2)
.s("sawtooth square")
.cutoff("<2000 1000 500>")
.out()
`;
export const waa2 = `n(
"a4 [a3 c3] a3 c3"
.sub("<7 12 5 12>".slow(2))
.off(1/4,x=>x.add(7))
.off(1/8,x=>x.add(12))
)
.slow(2)
.legato(sine.range(0.3, 2).slow(28))
.s("sawtooth square".fast(2))
.cutoff(cosine.range(500,4000).slow(16))
.out()`;
export const hyperpop = `const lfo = cosine.slow(15);
const lfo2 = sine.slow(16);
const filter1 = x=>x.filter('lowpass', lfo2.range(300,3000));
const filter2 = x=>x.filter('highpass', lfo.range(1000,6000)).filter('lowpass',4000)
const filter1 = x=>x.cutoff(lfo2.range(300,3000));
const filter2 = x=>x.hcutoff(lfo.range(1000,6000)).cutoff(4000)
const scales = cat('D3 major', 'G3 major').slow(8)
const drums = await players({
@@ -765,24 +779,30 @@ const drums = await players({
stack(
"-7 0 -7 7".struct("x(5,8,2)").fast(2).sub(7)
.scale(scales).wave("sawtooth,square").velocity(.3).adsr(0.01,0.1,.5,0)
.scale(scales)
.n()
.s("sawtooth,square")
.gain(.3).attack(0.01).decay(0.1).sustain(.5)
.apply(filter1),
"~@3 [<2 3>,<4 5>]"
.echo(8,1/16,.7)
.echo(4,1/16,.7)
.scale(scales)
.wave('square').velocity(.7).adsr(0.01,0.1,0).apply(filter1),
"6 5 4".add(14)
.n()
.s('square').gain(.7)
.attack(0.01).decay(0.1).sustain(0)
.apply(filter1),
"6 4 2".add(14)
.superimpose(sub("5"))
.fast(1).euclidLegato(3,8)
.mask("<1 0@7>")
.fast(2)
.echo(32, 1/8, .9)
.echo(32, 1/8, .8)
.scale(scales)
.wave("sawtooth")
.velocity(.2)
.adsr(.01,.5,0)
.n()
.s("sawtooth")
.gain(sine.range(.1,.4).slow(8))
.attack(.001).decay(.2).sustain(0)
.apply(filter2)
//.echo(4,1/16,.5)
).out().stack(
stack(
"bd <~@7 [~ bd]>".fast(2),
@@ -790,7 +810,7 @@ stack(
"[~ hh3]*2"
).tone(drums.chain(vol(.18),out())).fast(2)
).slow(2)
//.pianoroll({minMidi:20, maxMidi:160})
// strudel disable-highlighting`;
@@ -816,3 +836,62 @@ stack(
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings(),
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
).tone((await piano()).toDestination()).slow(2)`;
export const customTrigger = `stack(
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
freq("440(5,8)".legato(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
).s("<sawtooth square>/2")
.onTrigger((t,hap,ct)=>{
const ac = Tone.getContext().rawContext;
t = ac.currentTime + t - ct;
const { freq, s, gain = 1 } = hap.value;
const master = ac.createGain();
master.gain.value = 0.1 * gain;
master.connect(ac.destination);
const o = ac.createOscillator();
o.type = s || 'triangle';
o.frequency.value = Number(freq);
o.connect(master);
o.start(t);
o.stop(t + hap.duration);
}).stack(s("bd(3,8),hh*4,~ sd").webdirt())`;
export const bornagain = `stack(
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)")
.layer(x=>x.mul("1.006,.995"))), // detune
freq("440(5,8)".legato(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
).s("<sawtooth square>/2")
.cutoff(perlin.range(100,4000).slow(4))
.jux(rev)
.out()
.stack(s("bd(3,8),hh*4,~ sd").webdirt())`;
export const meltingsubmarine = `samples({
clubkick: 'clubkick/2.wav',
sd: ['808sd/SD0010.WAV','808sd/SD0050.WAV'],
hh: 'hh/000_hh3closedhh.wav',
clak: 'clak/000_clak1.wav',
jvbass: ['jvbass/000_01.wav','jvbass/001_02.wav','jvbass/003_04.wav','jvbass/004_05.wav','jvbass/005_06.wav']
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
stack(
"<clubkick*2>,[~ <sd!3 sd(3,4,2)>],hh(3,4)".s().n("<0 1 2>").speed(perlin.range(.7,.9)),
"<a1 b1*2 a1(3,8) e2>"
.off(1/8,x=>x.add(12).degradeBy(.5))
.add(perlin.range(0,.5))
.n().decay(.15).sustain(0).s("sawtooth")
.superimpose(x=>x.add(.08)).gain(.4)
.cutoff(sine.slow(7).range(300,5000)),
"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings().superimpose(x=>x.add(.04))
.add(perlin.range(0,.5))
.n().s('sawtooth')
.gain(.16)
.cutoff(500)
.attack(1),
"a4 c5 <e6 a6>".struct("x(5,8)")
.superimpose(x=>x.add(.04))
.add(perlin.range(0,.5)).n()
.decay(.1).sustain(0).s('triangle')
.degradeBy(perlin.range(0,.5)).echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1)))
)
.out()
.slow(3/2)`;
File diff suppressed because one or more lines are too long
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+11
View File
@@ -0,0 +1,11 @@
# @strudel.cycles/starter
This is a minimal starter template for a project that uses strudel.
See main.js for the code.
Run with:
```js
npm i
npm run dev
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Strudel Starter</title>
</head>
<body style="margin: 0; padding: 0">
<div id="container" style="background-color: indigo; display: flex; justify-content: center; align-items: center; height: 100vh">
<center>
<h1>Strudel Starter Template</h1>
<button id="start" style="font-size: 3em">start</button>
</center>
</div>
<script type="module" src="/main.js"></script>
</body>
</html>
+99
View File
@@ -0,0 +1,99 @@
import { evaluate, evalScope } from '@strudel.cycles/eval';
import { Scheduler, getAudioContext } from '@strudel.cycles/webaudio';
import { loadWebDirt } from '@strudel.cycles/webdirt';
import controls from '@strudel.cycles/core/controls.mjs';
// import desired modules and add them to the eval scope
await evalScope(
import('@strudel.cycles/core'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/osc'),
import('@strudel.cycles/webdirt'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/tonal'),
controls,
// import other strudel packages here
); // add strudel to eval scope
const audioContext = getAudioContext();
const latency = 0.2;
// load default samples + init webdirt
loadWebDirt({
audioContext,
latency,
sampleMapUrl: 'https://strudel.tidalcycles.org/EmuSP12.json',
sampleFolder: 'https://strudel.tidalcycles.org/EmuSP12/',
});
// the scheduler will query the pattern within the given interval
const scheduler = new Scheduler({
audioContext,
interval: 0.1,
latency,
onEvent: (hap) => {
/* const delta = hap.whole.begin - audioContext.currentTime;
console.log('delta', delta); */
// when using .osc or .webdirt, each hap will have context.onTrigger set
// if no onTrigger is set, try to play hap.value as frequency with a cheap oscillator
if (!hap.context.onTrigger) {
// console.log('e', e.show());
const oscillator = audioContext.createOscillator();
const master = audioContext.createGain();
master.gain.value = 0.1;
master.connect(audioContext.destination);
oscillator.type = 'sawtooth';
oscillator.frequency.value = hap.value;
oscillator.connect(master);
oscillator.start(hap.whole.begin);
oscillator.stop(hap.whole.end);
}
},
});
// eval + start on button click
document.getElementById('start').addEventListener('click', async () => {
const { pattern } = await evaluate(
// 's("bd sd").osc()', // need to run sclang + osc server (npm run osc in strudel root)
/* `stack(
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)")
.layer(x=>x.mul("1.006,.995"))), // detune
freq("440(5,8)".legato(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
).s("<sawtooth square>/2")
.cutoff(perlin.range(100,4000).slow(4))
.jux(rev)
.out()
.stack(s("bd(3,8),hh*4,~ sd").webdirt())`, */
`samples({
clubkick: 'clubkick/2.wav',
sd: ['808sd/SD0010.WAV','808sd/SD0050.WAV'],
hh: 'hh/000_hh3closedhh.wav',
clak: 'clak/000_clak1.wav',
jvbass: ['jvbass/000_01.wav','jvbass/001_02.wav','jvbass/003_04.wav','jvbass/004_05.wav','jvbass/005_06.wav']
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
stack(
"<clubkick*2>,[~ <sd!3 sd(3,4,2)>],hh(3,4)".s().n("<0 1 2>").speed(perlin.range(.7,.9)),
"<a1 b1*2 a1(3,8) e2>"
.off(1/8,x=>x.add(12).degradeBy(.5))
.add(perlin.range(0,.5))
.n().decay(.15).sustain(0).s("sawtooth")
.superimpose(x=>x.add(.08)).gain(.4)
.cutoff(sine.slow(7).range(300,5000)),
"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings().superimpose(x=>x.add(.04))
.add(perlin.range(0,.5))
.n().s('sawtooth')
.gain(.16)
.cutoff(500)
.attack(1),
"a4 c5 <e6 a6>".struct("x(5,8)")
.superimpose(x=>x.add(.04))
.add(perlin.range(0,.5)).n()
.decay(.1).sustain(0).s('triangle')
.degradeBy(perlin.range(0,.5)).echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1)))
)
.out()
.slow(3/2)`,
);
scheduler.setPattern(pattern);
scheduler.start();
});
+827
View File
@@ -0,0 +1,827 @@
{
"name": "@strudel.cycles/starter",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/starter",
"version": "0.0.1",
"devDependencies": {
"vite": "^2.9.9"
}
},
"node_modules/esbuild": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.44.tgz",
"integrity": "sha512-Rn+lRRfj60r/3svI6NgAVnetzp3vMOj17BThuhshSj/gS1LR03xrjkDYyfPmrYG/0c3D68rC6FNYMQ3yRbiXeQ==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"esbuild-android-64": "0.14.44",
"esbuild-android-arm64": "0.14.44",
"esbuild-darwin-64": "0.14.44",
"esbuild-darwin-arm64": "0.14.44",
"esbuild-freebsd-64": "0.14.44",
"esbuild-freebsd-arm64": "0.14.44",
"esbuild-linux-32": "0.14.44",
"esbuild-linux-64": "0.14.44",
"esbuild-linux-arm": "0.14.44",
"esbuild-linux-arm64": "0.14.44",
"esbuild-linux-mips64le": "0.14.44",
"esbuild-linux-ppc64le": "0.14.44",
"esbuild-linux-riscv64": "0.14.44",
"esbuild-linux-s390x": "0.14.44",
"esbuild-netbsd-64": "0.14.44",
"esbuild-openbsd-64": "0.14.44",
"esbuild-sunos-64": "0.14.44",
"esbuild-windows-32": "0.14.44",
"esbuild-windows-64": "0.14.44",
"esbuild-windows-arm64": "0.14.44"
}
},
"node_modules/esbuild-android-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.44.tgz",
"integrity": "sha512-dFPHBXmx385zuJULAD/Cmq/LyPRXiAWbf9ylZtY0wJ8iVyWfKYaCYxeJx8OAZUuj46ZwNa7MzW2GBAQLOeiemg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.44.tgz",
"integrity": "sha512-qqaqqyxHXjZ/0ddKU3I3Nb7lAvVM69ELMhb8+91FyomAUmQPlHtxe+TTiWxXGHE72XEzcgTEGq4VauqLNkN22g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.44.tgz",
"integrity": "sha512-RBmtGKGY06+AW6IOJ1LE/dEeF7HH34C1/Ces9FSitU4bIbIpL4KEuQpTFoxwb4ry5s2hyw7vbPhhtyOd18FH9g==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.44.tgz",
"integrity": "sha512-Bmhx5Cfo4Hdb7WyyyDupTB8HPmnFZ8baLfPlzLdYvF6OzsIbV+CY+m/AWf0OQvY40BlkzCLJ/7Lfwbb71Tngmg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.44.tgz",
"integrity": "sha512-O4HpWa5ZgxbNPQTF7URicLzYa+TidGlmGT/RAC3GjbGEQQYkd0R1Slyh69Yrmb2qmcOcPAgWHbNo1UhK4WmZ4w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.44.tgz",
"integrity": "sha512-f0/jkAKccnDY7mg1F9l/AMzEm+VXWXK6c3IrOEmd13jyKfpTZKTIlt+yI04THPDCDZTzXHTRUBLozqp+m8Mg5Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.44.tgz",
"integrity": "sha512-WSIhzLldMR7YUoEL7Ix319tC+NFmW9Pu7NgFWxUfOXeWsT0Wg484hm6bNgs7+oY2pGzg715y/Wrqi1uNOMmZJw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.44.tgz",
"integrity": "sha512-zgscTrCMcRZRIsVugqBTP/B5lPLNchBlWjQ8sQq2Epnv+UDtYKgXEq1ctWAmibZNy2E9QRCItKMeIEqeTUT5kA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.44.tgz",
"integrity": "sha512-laPBPwGfsbBxGw6F6jnqic2CPXLyC1bPrmnSOeJ9oEnx1rcKkizd4HWCRUc0xv+l4z/USRfx/sEfYlWSLeqoJQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.44.tgz",
"integrity": "sha512-H0H/2/wgiScTwBve/JR8/o+Zhabx5KPk8T2mkYZFKQGl1hpUgC+AOmRyqy/Js3p66Wim4F4Akv3I3sJA1sKg0w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.44.tgz",
"integrity": "sha512-ri3Okw0aleYy7o5n9zlIq+FCtq3tcMlctN6X1H1ucILjBJuH8pan2trJPKWeb8ppntFvE28I9eEXhwkWh6wYKg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.44.tgz",
"integrity": "sha512-96TqL/MvFRuIVXz+GtCIXzRQ43ZwEk4XTn0RWUNJduXXMDQ/V1iOV28U6x6Oe3NesK4xkoKSaK2+F3VHcU8ZrA==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.44.tgz",
"integrity": "sha512-rrK9qEp2M8dhilsPn4T9gxUsAumkITc1kqYbpyNMr9EWo+J5ZBj04n3GYldULrcCw4ZCHAJ+qPjqr8b6kG2inA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.44.tgz",
"integrity": "sha512-2YmTm9BrW5aUwBSe8wIEARd9EcnOQmkHp4+IVaO09Ez/C5T866x+ABzhG0bwx0b+QRo9q97CRMaQx2Ngb6/hfw==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.44.tgz",
"integrity": "sha512-zypdzPmZTCqYS30WHxbcvtC0E6e/ECvl4WueUdbdWhs2dfWJt5RtCBME664EpTznixR3lSN1MQ2NhwQF8MQryw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.44.tgz",
"integrity": "sha512-8J43ab9ByYl7KteC03HGQjr2HY1ge7sN04lFnwMFWYk2NCn8IuaeeThvLeNjzOYhyT3I6K8puJP0uVXUu+D1xw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.44.tgz",
"integrity": "sha512-OH1/09CGUJwffA+HNM6mqPkSIyHVC3ZnURU/4CCIx7IqWUBn1Sh1HRLQC8/TWNgcs0/1u7ygnc2pgf/AHZJ/Ow==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.44.tgz",
"integrity": "sha512-mCAOL9/rRqwfOfxTu2sjq/eAIs7eAXGiU6sPBnowggI7QS953Iq6o3/uDu010LwfN7zr18c/lEj6/PTwwTB3AA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.44.tgz",
"integrity": "sha512-AG6BH3+YG0s2Q/IfB1cm68FdyFnoE1P+GFbmgFO3tA4UIP8+BKsmKGGZ5I3+ZjcnzOwvT74bQRVrfnQow2KO5Q==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.44.tgz",
"integrity": "sha512-ygYPfYE5By4Sd6szsNr10B0RtWVNOSGmZABSaj4YQBLqh9b9i45VAjVWa8tyIy+UAbKF7WGwybi2wTbSVliO8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/is-core-module": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/resolve": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.8.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.75.6",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.6.tgz",
"integrity": "sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/vite": {
"version": "2.9.12",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.9.12.tgz",
"integrity": "sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==",
"dev": true,
"dependencies": {
"esbuild": "^0.14.27",
"postcss": "^8.4.13",
"resolve": "^1.22.0",
"rollup": "^2.59.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": ">=12.2.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
}
}
}
},
"dependencies": {
"esbuild": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.44.tgz",
"integrity": "sha512-Rn+lRRfj60r/3svI6NgAVnetzp3vMOj17BThuhshSj/gS1LR03xrjkDYyfPmrYG/0c3D68rC6FNYMQ3yRbiXeQ==",
"dev": true,
"requires": {
"esbuild-android-64": "0.14.44",
"esbuild-android-arm64": "0.14.44",
"esbuild-darwin-64": "0.14.44",
"esbuild-darwin-arm64": "0.14.44",
"esbuild-freebsd-64": "0.14.44",
"esbuild-freebsd-arm64": "0.14.44",
"esbuild-linux-32": "0.14.44",
"esbuild-linux-64": "0.14.44",
"esbuild-linux-arm": "0.14.44",
"esbuild-linux-arm64": "0.14.44",
"esbuild-linux-mips64le": "0.14.44",
"esbuild-linux-ppc64le": "0.14.44",
"esbuild-linux-riscv64": "0.14.44",
"esbuild-linux-s390x": "0.14.44",
"esbuild-netbsd-64": "0.14.44",
"esbuild-openbsd-64": "0.14.44",
"esbuild-sunos-64": "0.14.44",
"esbuild-windows-32": "0.14.44",
"esbuild-windows-64": "0.14.44",
"esbuild-windows-arm64": "0.14.44"
}
},
"esbuild-android-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.44.tgz",
"integrity": "sha512-dFPHBXmx385zuJULAD/Cmq/LyPRXiAWbf9ylZtY0wJ8iVyWfKYaCYxeJx8OAZUuj46ZwNa7MzW2GBAQLOeiemg==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.44.tgz",
"integrity": "sha512-qqaqqyxHXjZ/0ddKU3I3Nb7lAvVM69ELMhb8+91FyomAUmQPlHtxe+TTiWxXGHE72XEzcgTEGq4VauqLNkN22g==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.44.tgz",
"integrity": "sha512-RBmtGKGY06+AW6IOJ1LE/dEeF7HH34C1/Ces9FSitU4bIbIpL4KEuQpTFoxwb4ry5s2hyw7vbPhhtyOd18FH9g==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.44.tgz",
"integrity": "sha512-Bmhx5Cfo4Hdb7WyyyDupTB8HPmnFZ8baLfPlzLdYvF6OzsIbV+CY+m/AWf0OQvY40BlkzCLJ/7Lfwbb71Tngmg==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.44.tgz",
"integrity": "sha512-O4HpWa5ZgxbNPQTF7URicLzYa+TidGlmGT/RAC3GjbGEQQYkd0R1Slyh69Yrmb2qmcOcPAgWHbNo1UhK4WmZ4w==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.44.tgz",
"integrity": "sha512-f0/jkAKccnDY7mg1F9l/AMzEm+VXWXK6c3IrOEmd13jyKfpTZKTIlt+yI04THPDCDZTzXHTRUBLozqp+m8Mg5Q==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.44.tgz",
"integrity": "sha512-WSIhzLldMR7YUoEL7Ix319tC+NFmW9Pu7NgFWxUfOXeWsT0Wg484hm6bNgs7+oY2pGzg715y/Wrqi1uNOMmZJw==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.44.tgz",
"integrity": "sha512-zgscTrCMcRZRIsVugqBTP/B5lPLNchBlWjQ8sQq2Epnv+UDtYKgXEq1ctWAmibZNy2E9QRCItKMeIEqeTUT5kA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.44.tgz",
"integrity": "sha512-laPBPwGfsbBxGw6F6jnqic2CPXLyC1bPrmnSOeJ9oEnx1rcKkizd4HWCRUc0xv+l4z/USRfx/sEfYlWSLeqoJQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.44.tgz",
"integrity": "sha512-H0H/2/wgiScTwBve/JR8/o+Zhabx5KPk8T2mkYZFKQGl1hpUgC+AOmRyqy/Js3p66Wim4F4Akv3I3sJA1sKg0w==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.44.tgz",
"integrity": "sha512-ri3Okw0aleYy7o5n9zlIq+FCtq3tcMlctN6X1H1ucILjBJuH8pan2trJPKWeb8ppntFvE28I9eEXhwkWh6wYKg==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.44.tgz",
"integrity": "sha512-96TqL/MvFRuIVXz+GtCIXzRQ43ZwEk4XTn0RWUNJduXXMDQ/V1iOV28U6x6Oe3NesK4xkoKSaK2+F3VHcU8ZrA==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.44.tgz",
"integrity": "sha512-rrK9qEp2M8dhilsPn4T9gxUsAumkITc1kqYbpyNMr9EWo+J5ZBj04n3GYldULrcCw4ZCHAJ+qPjqr8b6kG2inA==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.44.tgz",
"integrity": "sha512-2YmTm9BrW5aUwBSe8wIEARd9EcnOQmkHp4+IVaO09Ez/C5T866x+ABzhG0bwx0b+QRo9q97CRMaQx2Ngb6/hfw==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.44.tgz",
"integrity": "sha512-zypdzPmZTCqYS30WHxbcvtC0E6e/ECvl4WueUdbdWhs2dfWJt5RtCBME664EpTznixR3lSN1MQ2NhwQF8MQryw==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.44.tgz",
"integrity": "sha512-8J43ab9ByYl7KteC03HGQjr2HY1ge7sN04lFnwMFWYk2NCn8IuaeeThvLeNjzOYhyT3I6K8puJP0uVXUu+D1xw==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.44.tgz",
"integrity": "sha512-OH1/09CGUJwffA+HNM6mqPkSIyHVC3ZnURU/4CCIx7IqWUBn1Sh1HRLQC8/TWNgcs0/1u7ygnc2pgf/AHZJ/Ow==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.44.tgz",
"integrity": "sha512-mCAOL9/rRqwfOfxTu2sjq/eAIs7eAXGiU6sPBnowggI7QS953Iq6o3/uDu010LwfN7zr18c/lEj6/PTwwTB3AA==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.44.tgz",
"integrity": "sha512-AG6BH3+YG0s2Q/IfB1cm68FdyFnoE1P+GFbmgFO3tA4UIP8+BKsmKGGZ5I3+ZjcnzOwvT74bQRVrfnQow2KO5Q==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.14.44",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.44.tgz",
"integrity": "sha512-ygYPfYE5By4Sd6szsNr10B0RtWVNOSGmZABSaj4YQBLqh9b9i45VAjVWa8tyIy+UAbKF7WGwybi2wTbSVliO8A==",
"dev": true,
"optional": true
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"is-core-module": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
"dev": true,
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"resolve": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
"dev": true,
"requires": {
"is-core-module": "^2.8.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"rollup": {
"version": "2.75.6",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.6.tgz",
"integrity": "sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"vite": {
"version": "2.9.12",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.9.12.tgz",
"integrity": "sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==",
"dev": true,
"requires": {
"esbuild": "^0.14.27",
"fsevents": "~2.3.2",
"postcss": "^8.4.13",
"resolve": "^1.22.0",
"rollup": "^2.59.0"
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"name": "@strudel.cycles/starter",
"private": true,
"version": "0.0.1",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^2.9.9"
},
"dependencies": {}
}