diff --git a/website/src/docs/Icon.jsx b/website/src/docs/Icon.jsx
index 64d5f88a..91583e50 100644
--- a/website/src/docs/Icon.jsx
+++ b/website/src/docs/Icon.jsx
@@ -1,4 +1,12 @@
export function Icon({ type }) {
+ if (type === 'skip') {
+ // !Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.
+ return (
+
+ );
+ }
return (
diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx
index 33dadedf..10eff483 100644
--- a/website/src/docs/MiniRepl.jsx
+++ b/website/src/docs/MiniRepl.jsx
@@ -1,8 +1,8 @@
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
import { Icon } from './Icon';
-import { silence, getPunchcardPainter, noteToMidi } from '@strudel/core';
+import { silence, getPunchcardPainter, noteToMidi, _mod } from '@strudel/core';
import { transpiler } from '@strudel/transpiler';
-import { getAudioContext, webaudioOutput } from '@strudel/webaudio';
+import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio';
import { StrudelMirror } from '@strudel/codemirror';
// import { prebake } from '@strudel/repl';
import { prebake } from '../repl/prebake.mjs';
@@ -10,14 +10,16 @@ import { loadModules } from '../repl/util.mjs';
import Claviature from '@components/Claviature';
import useClient from '@src/useClient.mjs';
-let prebaked, modulesLoading;
+let prebaked, modulesLoading, audioLoading;
if (typeof window !== 'undefined') {
prebaked = prebake();
modulesLoading = loadModules();
+ audioLoading = initAudioOnFirstClick();
}
export function MiniRepl({
- tune: code,
+ tune,
+ tunes,
hideHeader = false,
canvasHeight = 100,
onTrigger,
@@ -26,6 +28,7 @@ export function MiniRepl({
claviature,
claviatureLabels,
}) {
+ const code = tunes ? tunes[0] : tune;
const id = useMemo(() => s4(), []);
const canvasId = useMemo(() => `canvas-${id}`, [id]);
const shouldDraw = !!punchcard || !!claviature;
@@ -75,7 +78,7 @@ export function MiniRepl({
}
return pat;
},
- prebake: async () => Promise.all([modulesLoading, prebaked]),
+ prebake: async () => Promise.all([modulesLoading, prebaked, audioLoading]),
onUpdateState: (state) => {
setReplState({ ...state });
},
@@ -91,6 +94,14 @@ export function MiniRepl({
const containerRef = useRef();
const client = useClient();
+ const [tuneIndex, setTuneIndex] = useState(0);
+ const changeTune = (index) => {
+ index = _mod(index, tunes.length);
+ setTuneIndex(index);
+ editorRef.current?.setCode(tunes[index]);
+ editorRef.current?.evaluate();
+ };
+
if (!client) {
return
{code};
}
@@ -119,6 +130,28 @@ export function MiniRepl({
+ {tunes && (
+
+
+
+
+ )}
)}
diff --git a/website/src/examples.mjs b/website/src/examples.mjs
new file mode 100644
index 00000000..d752e711
--- /dev/null
+++ b/website/src/examples.mjs
@@ -0,0 +1,81 @@
+export const examples = [
+ `// "coastline" @by eddyflux
+await samples('github:eddyflux/crate')
+setcps(.75)
+let chords = chord("
/4").dict('ireal')
+stack(
+ stack( // DRUMS
+ s("bd").struct("<[x*<1 2> [~@3 x]] x>"),
+ s("~ [rim, sd:<2 3>]").room("<0 .2>"),
+ n("[0 <1 3>]*<2!3 4>").s("hh"),
+ s("rd:<1!3 2>*2").mask("<0 0 1 1>/16").gain(.5)
+ ).bank('crate')
+ .mask("<[0 1] 1 1 1>/16".early(.5))
+ , // CHORDS
+ chords.offset(-1).voicing().s("gm_epiano1:1")
+ .phaser(4).room(.5)
+ , // MELODY
+ n("<0!3 1*2>").set(chords).mode("root:g2")
+ .voicing().s("gm_acoustic_bass"),
+ chords.n("[0 <4 3 <2 5>>*2](<3 5>,8)")
+ .set(x).anchor("D5").voicing()
+ .segment(4).clip(rand.range(.4,.8))
+ .room(.75).shape(.3).delay(.25)
+ .fm(sine.range(3,8).slow(8))
+ .lpf(sine.range(500,1000).slow(8)).lpq(5)
+ .rarely(ply("2")).chunk(4, fast(2))
+ .gain(perlin.range(.6, .9))
+ .mask("<0 1 1 0>/16")
+)
+.late("[0 .01]*4").late("[0 .01]*2").size(4)`,
+ `// "broken cut 1" @by froos
+
+await samples('github:tidalcycles/Dirt-Samples/master')
+samples({
+ 'slap': 'https://cdn.freesound.org/previews/495/495416_10350281-lq.mp3',
+ 'whirl': 'https://cdn.freesound.org/previews/495/495313_10350281-lq.mp3',
+ 'attack': 'https://cdn.freesound.org/previews/494/494947_10350281-lq.mp3'
+})
+
+setcps(1.25)
+
+note("[c2 ~](3,8)*2,eb,g,bb,d").s("sawtooth")
+ .noise(0.3)
+ .lpf(perlin.range(800,2000).mul(0.6))
+ .lpenv(perlin.range(1,5)).lpa(.25).lpd(.1).lps(0)
+ .add.mix(note("<0!3 [1 <4!3 12>]>")).late(.5)
+ .vib("4:.2")
+ .room(1).roomsize(4).slow(4)
+ .stack(
+ s("bd").late("<0.01 .251>"),
+ s("breaks165:1/2").fit()
+ .chop(4).sometimesBy(.4, ply("2"))
+ .sometimesBy(.1, ply("4")).release(.01)
+ .gain(1.5).sometimes(mul(speed("1.05"))).cut(1)
+ ,
+ s("?").delay(".8:.1:.8").room(2).slow(8).cut(2),
+ ).reset("".late(2))`,
+ `// "acidic tooth" @by eddyflux
+ setcps(1)
+ stack(
+ note("[/8](<3 5>,8)")
+ .clip(perlin.range(.15,1.5))
+ .release(.1)
+ .s("sawtooth")
+ .lpf(sine.range(400,800).slow(16))
+ .lpq(cosine.range(6,14).slow(3))
+ .lpenv(sine.mul(4).slow(4))
+ .lpd(.2).lpa(.02)
+ .ftype('24db')
+ .rarely(add(note(12)))
+ .room(.2).shape(.3).postgain(.5)
+ .superimpose(x=>x.add(note(12)).delay(.5).bpf(1000))
+ .gain("[.2 1@3]*2") // fake sidechain
+ ,
+ stack(
+ s("bd*2").mask("<0@4 1@16>"),
+ s("hh*8").gain(saw.mul(saw.fast(2))).clip(sine)
+ .mask("<0@8 1@16>")
+ ).bank('RolandTR909')
+ )`,
+];
diff --git a/website/src/pages/workshop/getting-started.mdx b/website/src/pages/workshop/getting-started.mdx
index 3d37a5cf..83dc8894 100644
--- a/website/src/pages/workshop/getting-started.mdx
+++ b/website/src/pages/workshop/getting-started.mdx
@@ -4,6 +4,7 @@ layout: ../../layouts/MainLayout.astro
---
import { MiniRepl } from '../../docs/MiniRepl';
+import { examples } from '../../examples.mjs';
# Welcome
@@ -29,40 +30,13 @@ The best place to actually make music with Strudel is the [Strudel REPL](https:/
- teaching: focussing on a low barrier of entry, Strudel is a good fit for teaching music and code at the same time.
- integrate into your existing music setup: either via MIDI or OSC, you can use Strudel as a really flexible sequencer
-## Example
+## Examples
-Here is an example of how strudel can sound:
+Here are some examples of how strudel can sound:
-],hh*8")
- .speed(perlin.range(.8,.9)), // random sample speed variation
- // bassline
- ""
- .off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
- .add(perlin.range(0,.5)) // random pitch variation
- .superimpose(add(.05)) // add second, slightly detuned voice
- .note() // wrap in "note"
- .decay(.15).sustain(0) // make each note of equal length
- .s('sawtooth') // waveform
- .gain(.4) // turn down
- .cutoff(sine.slow(7).range(300,5000)), // automate cutoff
- // chords
- ">".voicings('lefthand')
- .superimpose(x=>x.add(.04)) // add second, slightly detuned voice
- .add(perlin.range(0,.5)) // random pitch variation
- .note() // wrap in "note"
- .s('sawtooth') // waveform
- .gain(.16) // turn down
- .cutoff(500) // fixed cutoff
- .attack(1) // slowly fade in
-)
-.slow(3/2)`}
-/>
+
-To hear more, go to the [Strudel REPL](https://strudel.cc/) and press shuffle to hear a random example pattern.
+These examples cannot fully encompass the variety of things you can do, so [check out the showcase](/intro/showcase/) for some videos of how people use Strudel.
## Getting Started