diff --git a/src/content/strudel/index.html b/src/content/strudel/index.html new file mode 100644 index 00000000..91f05bcc --- /dev/null +++ b/src/content/strudel/index.html @@ -0,0 +1,78 @@ + + +
+ + +${error.message}
+
+
++ Felix Roos & Team +
+ ++Strudel, entwickelt von einem Team um Felix Roos und erstmals 2021 veröffentlicht, ist eine domänenspezifische Programmiersprache für Live-Coding und algorithmische Musik. Sie überträgt das Konzept von TidalCycles in die JavaScript-Welt und ermöglicht es, Musik in Echtzeit zu programmieren, zu manipulieren und direkt im Browser auszuführen. +
+ +Bekannte Anwendungen von Strudel sind Live-Coding Performances, algorithmische Musik und Musikpädagogik. Besonders relevant ist Strudel, da keine Installation erforderlich ist und die Sprache kontinuierlich um neue Synthesizer, Effekte und Pattern-Funktionen erweitert wird. + +**Was ist Live-Coding?** Live-Coding bedeutet, dass du Musik schreibst, während sie gleichzeitig läuft. Du tippst Code und hörst sofort das Ergebnis – wie ein Instrument, das man mit der Tastatur spielt. Strudel läuft komplett im Browser, du musst also nichts installieren oder einrichten. + +--- + +## Hello, beats! + +Strudel-Programme werden direkt im Browser geschrieben und ausgeführt. Du brauchst keine Entwicklungsumgebung zu installieren – öffne einfach das Editor-Fenster und starte sofort. + + +### Deinen ersten Beat programmieren + +Schreibe im Editor-Fenster: + +
+s("bd sd")
+
+
+`s(...)` steht für „sound" – du sagst Strudel, welche Klänge es abspielen soll. `"bd sd"` sind zwei Drum-Klänge: **bd** steht für Bass Drum (der tiefe Grundschlag) und **sd** für Snare Drum (der hellere Gegenschlag). Strudel wiederholt diese Sequenz automatisch in einer Endlosschleife.
+
+Klicke auf den Play-Button oder drücke Strg + Enter, um den Code auszuführen.
+Um den Beat zu stoppen, drücke Strg + .
+
+### Erste Sounds ausprobieren
+
+
+sound("casio")
+
+
+Strudel hat viele eingebaute Klangvorlagen, sogenannte Samples. `casio` klingt wie ein altes Casio-Keyboard aus den 80ern. Du kannst einfach andere Wörter ausprobieren – wenn Strudel den Sound kennt, spielt er ihn ab. Probiere zum Beispiel `metal`, `piano` oder `jazz`.
+
+Ändere `casio` in `metal` und drücke wieder Strg + Enter.
+
+Samples wechseln:
+
+
+sound("casio:1")
+
+
+Viele Sounds gibt es in mehreren Varianten. Mit `:0`, `:1`, `:2` usw. wählst du eine bestimmte Version aus. Probiere verschiedene Zahlen, um andere Varianten desselben Klangs zu hören.
+
+### Drum Sounds
+
+
+sound("bd hh sd oh")
+
+
+Strudel verwendet kurze Kürzel für typische Schlagzeug-Elemente:
+
+- bd = Bass Drum – der tiefe Grundschlag
+- sd = Snare Drum – der hellere Gegenschlag
+- hh = HiHat – das kurze, metallische Zischen
+- oh = Open HiHat – wie hh, aber offen und länger ausklingend
+- rim = Rimshot – Schlag auf den Rand der Snare
+- lt = Low Tom – tiefer Trommelklang
+- mt = Middle Tom – mittlerer Trommelklang
+- ht = High Tom – hoher Trommelklang
+- rd = Ride Cymbal – metallisches Becken für gleichmäßige Rhythmen
+- cr = Crash Cymbal – das laute Akzent-Becken
+
+### Sequenzen / Patterns
+
+
+sound("bd hh sd hh")
+sound("")
+sound("*8")
+setcpm(90/4)
+sound("*8")
+
+
+Leerzeichen zwischen Sounds teilen einen Takt gleichmäßig auf – 4 Sounds ergeben 4 gleichlange Schritte. Spitze Klammern `< >` bedeuten: Strudel wählt bei jedem Takt abwechselnd einen anderen Klang aus der Liste aus. Der Stern `*8` bedeutet, das Pattern 8× schneller abzuspielen, also mehr Schläge pro Takt. `setcpm(90/4)` setzt das Tempo – CPM steht für Cycles per Minute, und `90/4` entspricht einem typischen 90-BPM-Groove.
+
+### Pausen, Untersequenzen und Parallel-Sequenzen
+
+
+sound("bd hh - rim")
+sound("bd [hh hh] rim [hh hh]")
+sound("bd hh*2 sd hh*3")
+sound("bd hh*16 sd hh*8")
+sound("hh hh hh, bd casio")
+sound("hh hh hh, bd bd, - casio")
+sound("hh hh hh, bd [bd,casio]")
+
+
+Ein Bindestrich `-` steht für eine Pause – an dieser Stelle im Takt ist Stille. Eckige Klammern `[ ]` fassen mehrere Sounds zu einer Untersequenz zusammen, die in denselben Zeitslot gequetscht wird. Ein Komma `,` spielt zwei oder mehr Sequenzen gleichzeitig ab – so baust du mehrere Spuren übereinander.
+
+---
+
+# Erste Effekte
+
+## Ein paar grundlegende Effekte
+
+**low-pass filter**
+
+
+note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
+.sound("sawtooth").lpf(800)
+
+
+`lpf` steht für Low-Pass-Filter. Er lässt nur tiefe Frequenzen durch und schneidet hohe ab – niedrige Werte (z.B. 200) klingen dumpf und dunkel, hohe Werte (z.B. 5000) klingen hell und offen. Probiere verschiedene Zahlen aus, um den Unterschied zu hören.
+
+**Filter automatisieren**
+
+
+note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
+.sound("sawtooth").lpf("200 1000")
+
+
+Statt eines festen Werts kannst du dem Filter ein Pattern übergeben. Hier wechselt der Filter zwischen 200 und 1000 – der Klang öffnet und schließt sich rhythmisch.
+
+**Vokal-Sound**
+
+
+note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
+.sound("sawtooth").vowel("/2")
+
+
+Der `vowel`-Effekt formt den Klang so, als würde eine Stimme einen Vokal singen. Mit einem Pattern wie `` wechselt der Klang rhythmisch zwischen verschiedenen Vokalformanten.
+
+**Gain (Lautstärke)**
+
+
+stack(
+ sound("hh*8").gain("[.25 1]*2"),
+ sound("bd*2,~ sd:1")
+)
+
+
+`gain` steuert die Lautstärke – 0 ist Stille, 1 ist volle Lautstärke. Mit einem Pattern wie `[.25 1]*2` erzeugst du einen rhythmischen Akzent, bei dem jede zweite Note lauter ist.
+
+**Stack kombinieren**
+
+
+stack(
+ stack(
+ sound("hh*8").gain("[.25 1]*2"),
+ sound("bd*2,~ sd:1")
+ ),
+ note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
+ .sound("sawtooth").lpf("200 1000"),
+ note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
+ .sound("sawtooth").vowel("/2")
+)
+
+
+`stack(...)` spielt mehrere Patterns gleichzeitig ab – es ist das Herzstück, um vollständige Songs zusammenzubauen. Du kannst so viele Ebenen schichten wie du möchtest.
+
+**ADSR-Hüllkurve**
+
+
+note("")
+.sound("sawtooth").lpf(600)
+.attack(.1)
+.decay(.1)
+.sustain(.25)
+.release(.2)
+
+
+Die ADSR-Hüllkurve bestimmt, wie sich die Lautstärke eines Klangs über die Zeit verhält. **Attack** ist die Zeit, bis der Klang seine volle Lautstärke erreicht. **Decay** ist, wie schnell er danach abfällt. **Sustain** ist die Lautstärke, auf der er sich hält, solange der Ton gehalten wird. **Release** ist, wie lange er nach dem Loslassen noch ausklingt. Alle Werte sind in Sekunden angegeben.
+
+**Delay**
+
+
+stack(
+ note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
+ .sound("gm_electric_guitar_muted"),
+ sound("").bank("RolandTR707")
+).delay(".5")
+
+
+`delay` erzeugt ein Echo – der Klang wird verzögert ein oder mehrere Male wiederholt. Der Wert bestimmt die Lautstärke des Echos: 0 = kein Echo, 1 = sehr starkes Echo. Werte um 0.3–0.5 klingen oft musikalisch interessant.
+
+**Room / Reverb**
+
+
+n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
+.scale("D4:minor").sound("gm_accordion:2")
+.room(2)
+
+
+`room` simuliert einen Raumklang – als würde der Sound in einem Raum oder einer Halle gespielt. Kleine Werte (0.1–0.5) klingen wie ein kleines Zimmer, große Werte (1–4) wie eine große Halle oder Kathedrale.
+
+**Panorama und Geschwindigkeit**
+
+
+sound("numbers:1 numbers:2 numbers:3 numbers:4")
+.pan("0 0.3 .6 1")
+.slow(2)
+
+sound("bd rim").speed("<1 2 -1 -2>")
+sound("bd*2,~ rim").slow(2)
+sound("[bd*2,~ rim]*<1 [2 4]>")
+
+
+`pan` verteilt Klänge im Stereo-Panorama: 0 = ganz links, 0.5 = Mitte, 1 = ganz rechts. `.slow(2)` verlangsamt das Pattern auf die halbe Geschwindigkeit. `speed` ändert die Abspielgeschwindigkeit eines Samples – negative Werte spielen das Sample rückwärts ab.
+
+**Automation mit Signalen**
+
+
+sound("hh*16").gain(sine)
+sound("hh*8").lpf(saw.range(500, 2000))
+note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
+.sound("sawtooth")
+.lpf(sine.range(100, 2000).slow(8))
+
+
+Statt fester Werte kannst du Signale wie `sine` oder `saw` verwenden, die sich kontinuierlich über die Zeit verändern. `sine` schwingt weich auf und ab wie eine Sinuswelle, `saw` steigt linear an und springt dann zurück. Mit `.range(min, max)` legst du fest, zwischen welchen Werten das Signal schwingen soll – so entstehen lebendige, sich bewegende Klänge ganz ohne manuelle Automation.
+
+## Rückblick
+
+| Effekt | Beispiel |
+| ------- | ---------------------------------------------------------------------------------------- |
+| lpf | note("c2 c3").sound("sawtooth").lpf("<400 2000>") |
+| vowel | note("c3 eb3 g3").sound("sawtooth").vowel("") |
+| gain | sound("hh*8").gain("[.25 1]*2") |
+| delay | sound("bd rim").delay(.5) |
+| room | sound("bd rim").room(.5) |
+| pan | sound("bd rim").pan("0 1") |
+| speed | sound("bd rim").speed("<1 2 -1 -2>") |
+| range | sound("hh*16").lpf(saw.range(200,4000)) |
diff --git a/src/content/strudel/style.css b/src/content/strudel/style.css
new file mode 100644
index 00000000..3b6727a7
--- /dev/null
+++ b/src/content/strudel/style.css
@@ -0,0 +1,297 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ background: #151514;
+ color: #e7e6e1;
+ font-family:
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
+ Cantarell, sans-serif;
+ height: 100vh;
+ overflow: hidden;
+}
+
+/* Split Layout */
+.split-layout {
+ display: flex;
+ height: 100vh;
+ width: 100vw;
+}
+
+/* Left Panel - Markdown Viewer */
+.markdown-panel {
+ width: 45%;
+ background: #121212;
+ border-right: 1px solid #333;
+ overflow-y: auto;
+ padding: 2rem 2.5rem;
+ scrollbar-width: thin;
+ scrollbar-color: #3a4048 #151514;
+}
+
+.markdown-panel::-webkit-scrollbar {
+ width: 8px;
+}
+
+.markdown-panel::-webkit-scrollbar-track {
+ background: #151514;
+}
+
+.markdown-panel::-webkit-scrollbar-thumb {
+ background: #3a4048;
+ border-radius: 4px;
+}
+
+/* Markdown Content Styling */
+.markdown-content {
+ font-family: Georgia, "Times New Roman", serif;
+ line-height: 1.8;
+ color: #ccccc9;
+}
+
+.markdown-content h1 {
+ font-family: Georgia, serif;
+ font-size: 2.4rem;
+ font-weight: bold;
+ color: #e7e6e1;
+ line-height: 1.2;
+ margin-bottom: 1rem;
+ border-bottom: 2px solid #0d95fd;
+ padding-bottom: 0.4rem;
+}
+
+.markdown-content h2 {
+ font-family: Georgia, serif;
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: #e7e6e1;
+ margin: 2.5rem 0 0.6rem;
+ padding-bottom: 0.3rem;
+ border-bottom: 1px solid #232626;
+}
+
+.markdown-content h3 {
+ font-family: -apple-system, sans-serif;
+ font-size: 0.9rem;
+ font-weight: 600;
+ color: #ccccc9;
+ margin: 1.5rem 0 0.4rem;
+}
+
+.markdown-content p {
+ font-size: 0.97rem;
+ color: #ccccc9;
+ margin: 0.6rem 0;
+ font-family: Georgia, serif;
+}
+
+.markdown-content ul,
+.markdown-content ol {
+ margin: 0.5rem 0 0.75rem 1.5rem;
+ font-family: Georgia, serif;
+}
+
+.markdown-content li {
+ font-size: 0.95rem;
+ color: #ccccc9;
+ margin: 0.25rem 0;
+}
+
+.markdown-content code {
+ font-family: "Courier New", monospace;
+ font-size: 0.85em;
+ background: rgba(243, 132, 175, 0.08);
+ border: 1px solid #555753;
+ padding: 0.1em 0.4em;
+ border-radius: 3px;
+ color: #e5185d;
+}
+
+.markdown-content pre {
+ background: #0a0a0a;
+ border: 1px solid #555753;
+ border-radius: 4px;
+ padding: 1rem;
+ overflow-x: auto;
+ margin: 1rem 0;
+}
+
+.markdown-content pre code {
+ background: none;
+ border: none;
+ padding: 0;
+ color: #e5185d;
+}
+
+.markdown-content blockquote {
+ border-left: 3px solid #dc3545;
+ padding: 0.8rem 1.1rem;
+ background: #0c0d0d;
+ margin: 1.2rem 0 1.75rem;
+ color: #ccccc9;
+ font-size: 0.94rem;
+ font-style: italic;
+ border-radius: 0 4px 4px 0;
+}
+
+.markdown-content hr {
+ border: none;
+ border-top: 2px solid #232626;
+ margin: 2.5rem 0;
+}
+
+.markdown-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1.5rem;
+ padding-bottom: 0.75rem;
+ border-bottom: 1px solid #232626;
+ font-family: "Courier New", monospace;
+ font-size: 0.85rem;
+}
+
+.markdown-header .file-info {
+ color: #dc3545;
+}
+
+.markdown-header .file-info i {
+ margin-right: 0.5rem;
+}
+
+.markdown-header .file-badge {
+ background: #0a0a0a;
+ border: 1px solid #555753;
+ border-radius: 4px;
+ padding: 0.25rem 0.75rem;
+ color: #e5185d;
+}
+
+.loading-indicator {
+ text-align: center;
+ padding: 3rem;
+ color: #6c757d;
+ font-style: italic;
+}
+
+.error-indicator {
+ text-align: center;
+ padding: 2rem;
+ color: #dc3545;
+ border: 1px solid #dc3545;
+ border-radius: 4px;
+ background: #0a0a0a;
+}
+
+/* Right Panel - Editor Only */
+.editor-panel {
+ width: 55%;
+ background: #0e0e0d;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.editor-header {
+ padding: 1rem 1.5rem;
+ background: #151514;
+ border-bottom: 1px solid #333;
+ font-family: "Courier New", monospace;
+ font-size: 0.85rem;
+ color: #e7e6e1;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.editor-header span i {
+ margin-right: 0.5rem;
+}
+
+.brand {
+ color: #dc3545;
+}
+
+.audio-status {
+ margin-right: 1rem;
+}
+
+.audio-status i {
+ color: #dc3545;
+}
+
+.play-button {
+ background: #dc3545;
+ color: #000;
+ border: none;
+ padding: 0.4rem 1rem;
+ border-radius: 4px;
+ font-weight: bold;
+ font-size: 0.85rem;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+.play-button:hover {
+ background: #dc3545;
+}
+
+.play-button i {
+ margin-right: 0.3rem;
+}
+
+.editor-container {
+ flex: 1;
+ overflow-y: auto;
+ padding: 1.5rem;
+ background: #151514;
+}
+
+.editor-tabs {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 0.5rem;
+}
+
+.editor-tab {
+ display: inline-block;
+ padding: 0.4rem 1rem;
+ background: #181818;
+ border: 1px solid #555753;
+ border-bottom: none;
+ border-radius: 4px 4px 0 0;
+ margin-right: 0.25rem;
+ font-size: 0.8rem;
+ color: #ccccc9;
+}
+
+.editor-tab.active {
+ background: #1a1e22;
+ color: #e7e6e1;
+ border-bottom: 2px solid #dc3545;
+}
+
+strudel-editor {
+ display: block;
+ margin-bottom: 1.5rem;
+ border-radius: 6px;
+ overflow: hidden;
+ border: 1px solid #555753;
+ background: #181818;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+}
+
+kbd {
+ background: #0a0a0a;
+ border: 1px solid #555753;
+ color: #e5185d;
+ padding: 0.2rem 0.4rem;
+ border-radius: 3px;
+ font-size: 0.8rem;
+ font-family: "Courier New", monospace;
+}
+