microrepl: ssr static code

This commit is contained in:
Felix Roos 2023-12-17 22:42:51 +01:00
parent 9519d286bd
commit e17df79133
2 changed files with 46 additions and 47 deletions

View File

@ -1,11 +1,10 @@
import { useState, useRef, useCallback, useMemo } from 'react';
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
import { Icon } from './Icon';
import { silence, getPunchcardPainter } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
import { StrudelMirror } from '@strudel/codemirror';
import { prebake } from '@strudel/repl';
import { useInView } from 'react-hook-inview';
export function MicroRepl({
code,
@ -65,21 +64,26 @@ export function MicroRepl({
editorRef.current = editor;
}, []);
const [ref, isVisible] = useInView({
threshold: 0.01,
onEnter: () => {
if (!editorRef.current) {
init({ code, shouldDraw });
}
},
});
const [replState, setReplState] = useState({});
const { started, isDirty, error } = replState;
const editorRef = useRef();
const containerRef = useRef();
const [client, setClient] = useState(false);
useEffect(() => {
setClient(true);
if (!editorRef.current) {
setTimeout(() => {
init({ code, shouldDraw });
});
}
}, []);
if (!client) {
return <pre>{code}</pre>;
}
return (
<div className="overflow-hidden rounded-t-md bg-background border border-lineHighlight" ref={ref}>
<div className="overflow-hidden rounded-t-md bg-background border border-lineHighlight">
{!hideHeader && (
<div className="flex justify-between bg-lineHighlight">
<div className="flex">
@ -117,11 +121,6 @@ export function MicroRepl({
if (el && el.width !== el.clientWidth) {
el.width = el.clientWidth;
}
//const ratio = el.clientWidth / canvasHeight;
//const targetWidth = Math.round(el.width * ratio);
//if (el.width !== targetWidth) {
// el.width = targetWidth;
//}
}}
></canvas>
)}

View File

@ -17,7 +17,7 @@ An arpeggio is when the notes of a chord are played in sequence.
We can either write the notes by hand:
<MicroRepl
client:only="react"
client:visible
code={`note("c eb g c4")
.clip(2).s("gm_electric_guitar_clean")`}
punchcard
@ -26,7 +26,7 @@ We can either write the notes by hand:
...or use scales:
<MicroRepl
client:only="react"
client:visible
code={`n("0 2 4 7").scale("C:minor")
.clip(2).s("gm_electric_guitar_clean")`}
punchcard
@ -35,7 +35,7 @@ We can either write the notes by hand:
...or chord symbols:
<MicroRepl
client:only="react"
client:visible
code={`n("0 1 2 3").chord("Cm").mode("above:c3").voicing()
.clip(2).s("gm_electric_guitar_clean")`}
punchcard
@ -44,7 +44,7 @@ We can either write the notes by hand:
...using off:
<MicroRepl
client:only="react"
client:visible
code={`"0"
.off(1/3, add(2))
.off(1/2, add(4))
@ -59,7 +59,7 @@ We can either write the notes by hand:
A sample can be looped and chopped like this:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit().chop(16)`}
punchcard
@ -70,7 +70,7 @@ The chops are not audible yet, because we're not doing any manipulation.
Let's add randmized doubling + reversing:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit().chop(16).cut(1)
.sometimesBy(.5, ply(2))
@ -81,7 +81,7 @@ s("amen/8").fit().chop(16).cut(1)
If we want to specify the order of samples, we can replace `chop` with `slice`:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit()
.slice(8, "<0 1 2 3 4*2 5 6 [6 7]>")
@ -92,7 +92,7 @@ s("amen/8").fit()
If we use `splice` instead of `slice`, the speed adjusts to the duration of the event:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:yaxu/clean-breaks/main')
s("amen")
.splice(8, "<0 1 2 3 4*2 5 6 [6 7]>")
@ -107,7 +107,7 @@ Note that we don't need `fit`, because `splice` will do that by itself.
A minimal filter envelope looks like this:
<MicroRepl
client:only="react"
client:visible
code={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth")
.lpf(400).lpa(.2).lpenv(4)
@ -117,7 +117,7 @@ A minimal filter envelope looks like this:
We can flip the envelope by setting `lpenv` negative + add some resonance `lpq`:
<MicroRepl
client:only="react"
client:visible
code={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth").lpq(8)
.lpf(400).lpa(.2).lpenv(-4)
@ -129,7 +129,7 @@ We can flip the envelope by setting `lpenv` negative + add some resonance `lpq`:
We can layer sounds by separating them with ",":
<MicroRepl
client:only="react"
client:visible
code={`note("<g1 bb1 d2 f1>")
.s("sawtooth, square") // <------
.scope()`}
@ -138,7 +138,7 @@ We can layer sounds by separating them with ",":
We can control the gain of individual sounds like this:
<MicroRepl
client:only="react"
client:visible
code={`note("<g1 bb1 d2 f1>")
.s("sawtooth, square:0:.5") // <--- "name:number:gain"
.scope()`}
@ -147,7 +147,7 @@ We can control the gain of individual sounds like this:
For more control over each voice, we can use `layer`:
<MicroRepl
client:only="react"
client:visible
code={`note("<g1 bb1 d2 f1>").layer(
x=>x.s("sawtooth").vib(4),
x=>x.s("square").add(note(12))
@ -162,7 +162,7 @@ With `layer`, you can use any pattern method available on each voice, so sky is
We can fatten a sound by adding a detuned version to itself:
<MicroRepl
client:only="react"
client:visible
code={`note("<g1 bb1 d2 f1>")
.add(note("0,.1")) // <------ chorus
.s("sawtooth").scope()`}
@ -175,7 +175,7 @@ Try out different values, or add another voice!
Here is a simple example of a polyrhythm:
<MicroRepl client:only="react" code={`s("bd*2,hh*3")`} punchcard />
<MicroRepl client:visible code={`s("bd*2,hh*3")`} punchcard />
A polyrhythm is when 2 different tempos happen at the same time.
@ -183,7 +183,7 @@ A polyrhythm is when 2 different tempos happen at the same time.
This is a polymeter:
<MicroRepl client:only="react" code={`s("<bd rim>,<hh hh oh>").fast(2)`} punchcard />
<MicroRepl client:visible code={`s("<bd rim>,<hh hh oh>").fast(2)`} punchcard />
A polymeter is when 2 different bar lengths play at the same tempo.
@ -191,7 +191,7 @@ A polymeter is when 2 different bar lengths play at the same tempo.
This is a phasing:
<MicroRepl client:only="react" code={`note("<C D G A Bb D C A G D Bb A>*[6,6.1]").piano()`} punchcard />
<MicroRepl client:visible code={`note("<C D G A Bb D C A G D Bb A>*[6,6.1]").piano()`} punchcard />
Phasing happens when the same sequence plays at slightly different tempos.
@ -200,7 +200,7 @@ Phasing happens when the same sequence plays at slightly different tempos.
Using `run` with `n`, we can rush through a sample bank:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla")`}
punchcard
@ -211,7 +211,7 @@ Often times, you'll hear the beginning of the phrase not where the pattern begin
In this case, I hear the beginning at the third sample, which can be accounted for with `early`.
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla").early(2/8)`}
/>
@ -219,7 +219,7 @@ In this case, I hear the beginning at the third sample, which can be accounted f
Let's add some randomness:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla").early(2/8)
.sometimes(mul(speed(1.5)))`}
@ -230,7 +230,7 @@ n(run(8)).s("ftabla").early(2/8)
We can emulate a pitch warbling effect like this:
<MicroRepl
client:only="react"
client:visible
code={`note("c4 bb f eb")
.add(note(perlin.range(0,.5))) // <------ warble
.clip(2).s("gm_electric_guitar_clean")`}
@ -241,7 +241,7 @@ We can emulate a pitch warbling effect like this:
There are a number of ways to change the sound duration. Using clip:
<MicroRepl
client:only="react"
client:visible
code={`note("f ab bb c")
.clip("<2 1 .5 .25>/2")`}
/>
@ -250,7 +250,7 @@ The value of clip is relative to the duration of each event.
We can also create overlaps using release:
<MicroRepl
client:only="react"
client:visible
code={`note("f ab bb c")
.release("<2 1 .5 .002>/2")`}
/>
@ -259,7 +259,7 @@ This will smoothly fade out each sound for the given number of seconds.
We could also make the notes shorter with decay / sustain:
<MicroRepl
client:only="react"
client:visible
code={`note("f ab bb c")
.decay("<.2 .1 .02>/2").sustain(0)`}
/>
@ -268,27 +268,27 @@ For now, there is a limitation where decay values that exceed the event duration
When using samples, we also have `.end` to cut relative to the sample length:
<MicroRepl client:only="react" code={`s("oh*4").end("<1 .5 .25 .1>")`} />
<MicroRepl client:visible code={`s("oh*4").end("<1 .5 .25 .1>")`} />
Compare that to clip:
<MicroRepl client:only="react" code={`s("oh*4").clip("<1 .5 .25 .1>")`} />
<MicroRepl client:visible code={`s("oh*4").clip("<1 .5 .25 .1>")`} />
or decay / sustain
<MicroRepl client:only="react" code={`s("oh*4").decay("<.2 .12 .06 .01>").sustain(0)`} />
<MicroRepl client:visible code={`s("oh*4").decay("<.2 .12 .06 .01>").sustain(0)`} />
## Wavetable Synthesis
You can loop a sample with `loop` / `loopEnd`:
<MicroRepl client:only="react" code={`note("<c eb g f>").s("bd").loop(1).loopEnd(.05).gain(.2)`} />
<MicroRepl client:visible code={`note("<c eb g f>").s("bd").loop(1).loopEnd(.05).gain(.2)`} />
This allows us to play the first 5% of the bass drum as a synth!
To simplify loading wavetables, any sample that starts with `wt_` will be looped automatically:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:bubobubobubobubo/dough-waveforms/main')
note("c eb g bb").s("wt_dbass").clip(2)`}
/>
@ -296,7 +296,7 @@ note("c eb g bb").s("wt_dbass").clip(2)`}
Running through different wavetables can also give interesting variations:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:bubobubobubobubo/dough-waveforms/main')
note("c2*8").s("wt_dbass").n(run(8))`}
/>
@ -304,7 +304,7 @@ note("c2*8").s("wt_dbass").n(run(8))`}
...adding a filter envelope + reverb:
<MicroRepl
client:only="react"
client:visible
code={`await samples('github:bubobubobubobubo/dough-waveforms/main')
note("c2*8").s("wt_dbass").n(run(8))
.lpf(perlin.range(200,2000).slow(8))