summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-27 09:30:32 +0300
committerPaul Buetow <paul@buetow.org>2026-04-27 09:30:32 +0300
commit06dd860ddc915031d2356faf23e73a16bb2b19cf (patch)
tree8239ca6d6810af2922b1bbc24048dc54eca9e159
parent91ac0b2136b5cdf19d30141b8575f21c06afbfd1 (diff)
refactor(generator): move theme sound data from Go code to embedded JSON files
- Generate sounds.json for each theme inside templates/themes/<name>/ - Load sound data at runtime from embedded FS via templates.ThemeSounds() - Replace ~1100 lines of hardcoded builder functions and presets with a 145-line loader (soundCache, initSoundCache, loadThemeSounds) - Update embed.go to include themes/*/sounds.json in the embedded FS - Update tests to use loadThemeSounds() instead of themeSoundPresets - Eliminates duplication between themeSoundPresets and JSON output Improves cohesion and DRY by keeping theme assets (CSS, JS, meta, sounds) in a single location per theme directory.
-rw-r--r--internal/generator/generator_test.go19
-rw-r--r--internal/generator/templates/embed.go10
-rw-r--r--internal/generator/templates/themes/aurora/sounds.json447
-rw-r--r--internal/generator/templates/themes/biomech/sounds.json492
-rw-r--r--internal/generator/templates/themes/brutalist/sounds.json342
-rw-r--r--internal/generator/templates/themes/cathedral/sounds.json572
-rw-r--r--internal/generator/templates/themes/cosmos/sounds.json497
-rw-r--r--internal/generator/templates/themes/dos/sounds.json367
-rw-r--r--internal/generator/templates/themes/matrix/sounds.json283
-rw-r--r--internal/generator/templates/themes/neon/sounds.json337
-rw-r--r--internal/generator/templates/themes/noir/sounds.json472
-rw-r--r--internal/generator/templates/themes/nukem/sounds.json367
-rw-r--r--internal/generator/templates/themes/ocean/sounds.json427
-rw-r--r--internal/generator/templates/themes/plasma/sounds.json372
-rw-r--r--internal/generator/templates/themes/retro/sounds.json612
-rw-r--r--internal/generator/templates/themes/retrofuture/sounds.json322
-rw-r--r--internal/generator/templates/themes/spaceage/sounds.json472
-rw-r--r--internal/generator/templates/themes/surveillance/sounds.json451
-rw-r--r--internal/generator/templates/themes/synthwave/sounds.json472
-rw-r--r--internal/generator/templates/themes/terminal/sounds.json381
-rw-r--r--internal/generator/templates/themes/tropicale/sounds.json382
-rw-r--r--internal/generator/templates/themes/volcano/sounds.json322
-rw-r--r--internal/generator/theme_sounds.go1196
23 files changed, 8444 insertions, 1170 deletions
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 50cd3d0..10116a2 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -94,15 +94,6 @@ func TestJSONStringOrNull(t *testing.T) {
}
}
-func TestThemeSoundPresetsMatchRegistry(t *testing.T) {
- t.Parallel()
- for name := range getThemeSet() {
- if _, ok := themeSoundPresets[name]; !ok {
- t.Errorf("theme %q has no sound preset in themeSoundPresets", name)
- }
- }
-}
-
func TestThemeSoundsJSONNonEmpty(t *testing.T) {
t.Parallel()
j := themeSoundsJSON("neon")
@@ -152,9 +143,9 @@ func TestThemeSoundPresetsAmbientPopulated(t *testing.T) {
t.Parallel()
for name := range getThemeSet() {
- preset, ok := themeSoundPresets[name]
- if !ok {
- t.Errorf("theme %q missing from themeSoundPresets", name)
+ preset, err := loadThemeSounds(name)
+ if err != nil {
+ t.Errorf("theme %q loadThemeSounds: %v", name, err)
continue
}
@@ -174,8 +165,8 @@ func TestThemeSoundPresetsAmbientValuesBounded(t *testing.T) {
t.Parallel()
for name := range getThemeSet() {
- preset, ok := themeSoundPresets[name]
- if !ok {
+ preset, err := loadThemeSounds(name)
+ if err != nil {
continue
}
diff --git a/internal/generator/templates/embed.go b/internal/generator/templates/embed.go
index ac3efbd..c5a4b22 100644
--- a/internal/generator/templates/embed.go
+++ b/internal/generator/templates/embed.go
@@ -1,6 +1,7 @@
// Package templates exposes the embedded HTML shell, shared CSS/JS bundles,
// and per-theme assets used by the generator. Themes live as directories
-// under templates/themes/<name>/ containing theme.css, theme.js, and meta.json.
+// under templates/themes/<name>/ containing theme.css, theme.js, meta.json,
+// and sounds.json.
// All assets are compiled into the binary via //go:embed so snonux still ships
// as a single self-contained executable.
package templates
@@ -13,7 +14,7 @@ import (
"sort"
)
-//go:embed shell.tmpl shared/*.tmpl shared/shared.css shared/shared.js themes/*/theme.css themes/*/theme.js themes/*/meta.json
+//go:embed shell.tmpl shared/*.tmpl shared/shared.css shared/shared.js themes/*/theme.css themes/*/theme.js themes/*/meta.json themes/*/sounds.json
var FS embed.FS
// Shell returns the body of shell.tmpl — the single page template used for
@@ -64,6 +65,11 @@ func ThemeMeta(name string) ([]byte, error) {
return FS.ReadFile(path.Join("themes", name, "meta.json"))
}
+// ThemeSounds returns the per-theme sounds.json bytes for the named theme.
+func ThemeSounds(name string) ([]byte, error) {
+ return FS.ReadFile(path.Join("themes", name, "sounds.json"))
+}
+
// ThemeNames returns a sorted list of available theme names derived from the
// directories present under templates/themes/.
func ThemeNames() ([]string, error) {
diff --git a/internal/generator/templates/themes/aurora/sounds.json b/internal/generator/templates/themes/aurora/sounds.json
new file mode 100644
index 0000000..1440fe9
--- /dev/null
+++ b/internal/generator/templates/themes/aurora/sounds.json
@@ -0,0 +1,447 @@
+{
+ "splash": {
+ "freqs": [
+ 392,
+ 493.88,
+ 587.33,
+ 783.99
+ ],
+ "spacing": 0.08,
+ "gain": 0.09,
+ "wave": "sine"
+ },
+ "nav": {
+ "freq": 261.63,
+ "wave": "triangle",
+ "dur": 0.085,
+ "gain": 0.1
+ },
+ "open": {
+ "wave": "sine",
+ "start": 390,
+ "end": 780,
+ "dur": 0.16,
+ "gain": 0.1
+ },
+ "close": {
+ "wave": "sine",
+ "start": 523,
+ "end": 196,
+ "dur": 0.18,
+ "gain": 0.09
+ },
+ "bounce": {
+ "wave": "triangle",
+ "start": 180,
+ "end": 90,
+ "dur": 0.12,
+ "gain": 0.09
+ },
+ "ambient": {
+ "normal": {
+ "bpm": 130,
+ "gain": 0.032,
+ "wave": "triangle",
+ "droneFreqs": [
+ 98,
+ 146.83,
+ 196
+ ],
+ "cutoffMin": 1200,
+ "cutoffMax": 5500,
+ "attack": 0.02,
+ "release": 0.1,
+ "melody": [
+ {
+ "freq": 98,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 196,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 98,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 146.8340935339148,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 196,
+ "dur": 1.77408,
+ "step": 0.6283200000000001
+ },
+ {
+ "freq": 246.9445257793951,
+ "dur": 1.1457600000000001,
+ "step": 0.60984
+ },
+ {
+ "freq": 293.6681870678296,
+ "dur": 0.5544,
+ "step": 0.60984
+ },
+ {
+ "freq": 783.99,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 880,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 987.77,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 1174.66,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 987.77,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 880,
+ "dur": 0.42504000000000003,
+ "step": 0.462
+ },
+ {
+ "freq": 783.99,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 82.41,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 164.82,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 82.41,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 123.47548620540732,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 164.81,
+ "dur": 1.77408,
+ "step": 0.6283200000000001
+ },
+ {
+ "freq": 195.99322462359845,
+ "dur": 1.1457600000000001,
+ "step": 0.60984
+ },
+ {
+ "freq": 246.9359893400459,
+ "dur": 0.5544,
+ "step": 0.60984
+ },
+ {
+ "freq": 659.25,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 783.99,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 880,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 987.77,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 880,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 783.99,
+ "dur": 0.42504000000000003,
+ "step": 0.462
+ },
+ {
+ "freq": 659.25,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 65.41,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 130.82,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 65.41,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 98.00426589850373,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 130.81,
+ "dur": 1.77408,
+ "step": 0.6283200000000001
+ },
+ {
+ "freq": 164.81027253674833,
+ "dur": 1.1457600000000001,
+ "step": 0.60984
+ },
+ {
+ "freq": 195.9935487262387,
+ "dur": 0.5544,
+ "step": 0.60984
+ },
+ {
+ "freq": 783.99,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 880,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 987.77,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 1174.66,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 1318.5,
+ "dur": 0.42504000000000003,
+ "step": 0.462
+ },
+ {
+ "freq": 1174.66,
+ "dur": 0.42504000000000003,
+ "step": 0.462
+ },
+ {
+ "freq": 73.42,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 146.84,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 73.42,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 110.00570558428596,
+ "dur": 0.3927,
+ "step": 0.462
+ },
+ {
+ "freq": 146.83,
+ "dur": 1.77408,
+ "step": 0.6283200000000001
+ },
+ {
+ "freq": 184.9942077560642,
+ "dur": 1.1457600000000001,
+ "step": 0.60984
+ },
+ {
+ "freq": 219.99642809780318,
+ "dur": 0.5544,
+ "step": 0.60984
+ },
+ {
+ "freq": 880,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 987.77,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 1174.66,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 1318.5,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 1174.66,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ },
+ {
+ "freq": 987.77,
+ "dur": 0.42504000000000003,
+ "step": 0.462
+ },
+ {
+ "freq": 880,
+ "dur": 0.21252000000000001,
+ "step": 0.231
+ }
+ ],
+ "drums": [
+ "kick",
+ "_",
+ "hat",
+ "kick",
+ "snare",
+ "_",
+ "hat",
+ "_",
+ "kick",
+ "kick",
+ "hat",
+ "_",
+ "snare",
+ "_",
+ "hat",
+ "clap"
+ ]
+ },
+ "wild": {
+ "bpm": 170,
+ "gain": 0.065,
+ "wave": "sawtooth",
+ "droneFreqs": [
+ 98,
+ 196,
+ 293.66,
+ 392
+ ],
+ "cutoffMin": 2200,
+ "cutoffMax": 7500,
+ "attack": 0.02,
+ "release": 0.05,
+ "detuneCents": 12,
+ "melody": [
+ {
+ "freq": 196,
+ "dur": 0.30005,
+ "step": 0.353
+ },
+ {
+ "freq": 392,
+ "dur": 0.30005,
+ "step": 0.353
+ },
+ {
+ "freq": 196,
+ "dur": 0.30005,
+ "step": 0.353
+ },
+ {
+ "freq": 293.6681870678296,
+ "dur": 0.30005,
+ "step": 0.353
+ },
+ {
+ "freq": 1567.98,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 1760,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 1975.53,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 2349.32,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 1975.53,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 1760,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 1567.98,
+ "dur": 0.08119,
+ "step": 0.08825
+ },
+ {
+ "freq": 1318.5,
+ "dur": 0.08119,
+ "step": 0.08825
+ }
+ ],
+ "drums": [
+ "kick",
+ "_",
+ "hat",
+ "kick",
+ "snare",
+ "_",
+ "hat",
+ "_",
+ "kick",
+ "kick",
+ "hat",
+ "_",
+ "snare",
+ "_",
+ "hat",
+ "clap"
+ ]
+ }
+ }
+}
diff --git a/internal/generator/templates/themes/biomech/sounds.json b/internal/generator/templates/themes/biomech/sounds.json
new file mode 100644
index 0000000..ecb54ae
--- /dev/null
+++ b/internal/generator/templates/themes/biomech/sounds.json
@@ -0,0 +1,492 @@
+{
+ "splash": {
+ "freqs": [
+ 164.81,
+ 246.94,
+ 311.13,
+ 466.16
+ ],
+ "spacing": 0.09,
+ "gain": 0.095,
+ "wave": "triangle"
+ },
+ "nav": {
+ "freq": 233.08,
+ "wave": "sine",
+ "dur": 0.085,
+ "gain": 0.1
+ },
+ "open": {
+ "wave": "triangle",
+ "start": 220,
+ "end": 523.25,
+ "dur": 0.18,
+ "gain": 0.095
+ },
+ "close": {
+ "wave": "sine",
+ "start": 392,
+ "end": 130.81,
+ "dur": 0.2,
+ "gain": 0.09
+ },
+ "bounce": {
+ "wave": "triangle",
+ "start": 160,
+ "end": 80,
+ "dur": 0.14,
+ "gain": 0.09
+ },
+ "ambient": {
+ "normal": {
+ "bpm": 150,
+ "gain": 0.032,
+ "wave": "sawtooth",
+ "droneFreqs": [
+ 69.3,
+ 110,
+ 138.59
+ ],
+ "cutoffMin": 1200,
+ "cutoffMax": 5500,
+ "attack": 0.02,
+ "release": 0.08,
+ "melody": [
+ {
+ "freq": 69.3,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 82.41205306968857,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 98.00499987245549,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 123.47856233425104,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 138.59,
+ "dur": 1.536,
+ "step": 0.544
+ },
+ {
+ "freq": 164.81221406822712,
+ "dur": 0.992,
+ "step": 0.528
+ },
+ {
+ "freq": 207.6503777843393,
+ "dur": 0.48,
+ "step": 0.528
+ },
+ {
+ "freq": 138.59,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 164.81221406822712,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 195.99585760928727,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 246.93930669413928,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 277.18,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 329.62442813645424,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 391.99171521857454,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 493.87861338827855,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 82.41,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 98.00255834737423,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 116.54533967516677,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 146.83792672389072,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 164.81,
+ "dur": 1.536,
+ "step": 0.544
+ },
+ {
+ "freq": 195.99322462359845,
+ "dur": 0.992,
+ "step": 0.528
+ },
+ {
+ "freq": 246.9359893400459,
+ "dur": 0.48,
+ "step": 0.528
+ },
+ {
+ "freq": 164.81,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 195.99322462359845,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 233.07653721470982,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 293.6580354734187,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 329.63,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 391.99834131834695,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 466.16721656504336,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 587.3338889212001,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 58.27,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 69.29509859120856,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 82.40622427948026,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 103.82533661207516,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 116.54,
+ "dur": 1.536,
+ "step": 0.544
+ },
+ {
+ "freq": 146.8311991547485,
+ "dur": 0.992,
+ "step": 0.528
+ },
+ {
+ "freq": 174.61270673920848,
+ "dur": 0.48,
+ "step": 0.528
+ },
+ {
+ "freq": 116.54,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 138.5901971824171,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 164.8124485589605,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 207.65067322415032,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 233.08,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 277.1803943648342,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 329.624897117921,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 415.30134644830065,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 73.42,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 87.31158638349979,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 103.83155974943264,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 130.81956777172744,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 146.83,
+ "dur": 1.536,
+ "step": 0.544
+ },
+ {
+ "freq": 174.61128069584953,
+ "dur": 0.992,
+ "step": 0.528
+ },
+ {
+ "freq": 219.99642809780318,
+ "dur": 0.48,
+ "step": 0.528
+ },
+ {
+ "freq": 146.83,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 174.61128069584953,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 207.64897736324158,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 261.62131756909207,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 293.66,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 349.22256139169906,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 415.29795472648317,
+ "dur": 0.17,
+ "step": 0.2
+ },
+ {
+ "freq": 523.2426351381841,
+ "dur": 0.17,
+ "step": 0.2
+ }
+ ],
+ "drums": [
+ "kick",
+ "_",
+ "hat",
+ "kick",
+ "_",
+ "snare",
+ "kick",
+ "_",
+ "_",
+ "kick",
+ "hat",
+ "_",
+ "snare",
+ "_",
+ "kick",
+ "hat"
+ ]
+ },
+ "wild": {
+ "bpm": 190,
+ "gain": 0.075,
+ "wave": "sawtooth",
+ "droneFreqs": [
+ 69.3,
+ 138.59,
+ 220,
+ 277.18
+ ],
+ "cutoffMin": 2400,
+ "cutoffMax": 7500,
+ "attack": 0.02,
+ "release": 0.04,
+ "detuneCents": 22,
+ "melody": [
+ {
+ "freq": 138.59,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 164.81221406822712,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 195.99585760928727,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 246.93930669413928,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 277.18,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 329.62442813645424,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 391.99171521857454,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 493.87861338827855,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 554.37,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 659.2607483440585,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 783.9975725727728,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 987.7750447509198,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 1108.73,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 1318.509604616967,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 1567.9810030099218,
+ "dur": 0.1343,
+ "step": 0.158
+ },
+ {
+ "freq": 1975.532271527477,
+ "dur": 0.1343,
+ "step": 0.158
+ }
+ ],
+ "drums": [
+ "kick",
+ "_",
+ "hat",
+ "kick",
+ "_",
+ "snare",
+ "kick",
+ "_",
+ "_",
+ "kick",
+ "hat",
+ "_",
+ "snare",
+ "_",
+ "kick",
+ "hat"
+ ]
+ }
+ }
+}
diff --git a/internal/generator/templates/themes/brutalist/sounds.json b/internal/generator/templates/themes/brutalist/sounds.json
new file mode 100644
index 0000000..ffafee1
--- /dev/null
+++ b/internal/generator/templates/themes/brutalist/sounds.json
@@ -0,0 +1,342 @@
+{
+ "splash": {
+ "freqs": [
+ 261.63,
+ 329.63,
+ 392,
+ 523.25
+ ],
+ "spacing": 0.09,
+ "gain": 0.1,
+ "wave": "square"
+ },
+ "nav": {
+ "freq": 440,
+ "wave": "square",
+ "dur": 0.06,
+ "gain": 0.11
+ },
+ "open": {
+ "wave": "square",
+ "start": 440,
+ "end": 880,
+ "dur": 0.15,
+ "gain": 0.1
+ },
+ "close": {
+ "wave": "square",
+ "start": 660,
+ "end": 220,
+ "dur": 0.15,
+ "gain": 0.09
+ },
+ "bounce": {
+ "wave": "square",
+ "start": 200,
+ "end": 100,
+ "dur": 0.1,
+ "gain": 0.1
+ },
+ "ambient": {
+ "normal": {
+ "bpm": 140,
+ "gain": 0.032,
+ "wave": "sawtooth",
+ "droneFreqs": [
+ 65.41,
+ 98,
+ 130.81
+ ],
+ "cutoffMin": 1200,
+ "cutoffMax": 5500,
+ "attack": 0.02,
+ "release": 0.1,
+ "melody": [
+ {
+ "freq": 65.41,
+ "dur": 0.096525,
+ "step": 0.2145
+ },
+ {
+ "freq": 65.41,
+ "dur": 0.096525,
+ "step": 0.2145
+ },
+ {
+ "freq": 65.41,
+ "dur": 0.0