From 6a7101672c3d4b3f02e88c5008e2b6b7cd894584 Mon Sep 17 00:00:00 2001 From: jawhinge Date: Mon, 29 Dec 2025 03:35:33 +0200 Subject: [PATCH] added generative audio, tailwind, improvements, adjustments --- .tool-versions | 1 + package-lock.json | 440 +++++++++++- package.json | 6 +- src/app.css | 14 +- src/app.html | 2 +- src/lib/audio/audio-effects.ts | 35 + src/lib/audio/chord-progressions.ts | 61 ++ src/lib/audio/instruments/arpSynth.ts | 16 + src/lib/audio/instruments/bassSynth.ts | 16 + src/lib/audio/instruments/noiseSynth.ts | 16 + src/lib/audio/instruments/padSynth.ts | 16 + src/lib/audio/instruments/pingSynth.ts | 21 + src/lib/audio/weather-mood.ts | 97 +++ src/lib/components/AudioVisualization.svelte | 196 ++++++ .../air-quality/AirQualityGen.svelte | 241 ++++++- src/lib/generators/weather/WeatherGen.svelte | 632 ++++++++++++------ src/routes/+page.svelte | 14 +- src/routes/on-out/+page.svelte | 19 +- src/routes/on-out/+page.ts | 4 +- 19 files changed, 1597 insertions(+), 250 deletions(-) create mode 100644 .tool-versions create mode 100644 src/lib/audio/audio-effects.ts create mode 100644 src/lib/audio/chord-progressions.ts create mode 100644 src/lib/audio/instruments/arpSynth.ts create mode 100644 src/lib/audio/instruments/bassSynth.ts create mode 100644 src/lib/audio/instruments/noiseSynth.ts create mode 100644 src/lib/audio/instruments/padSynth.ts create mode 100644 src/lib/audio/instruments/pingSynth.ts create mode 100644 src/lib/audio/weather-mood.ts create mode 100644 src/lib/components/AudioVisualization.svelte diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..ef93fec --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 24.12.0 diff --git a/package-lock.json b/package-lock.json index d786d8e..250de7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "hear-on-out", "version": "0.0.1", "dependencies": { + "p5": "^2.1.2", + "p5-svelte": "^3.1.2", "tonal": "^6.4.2", "tone": "^15.1.22" }, @@ -20,19 +22,21 @@ "@tailwindcss/vite": "^4.0.0", "@vitejs/plugin-basic-ssl": "^2.0.0", "@vitest/browser": "^3.2.3", + "autoprefixer": "^10.4.23", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", "eslint-plugin-svelte": "^3.0.0", "globals": "^16.0.0", "openmeteo": "^1.2.0", "playwright": "^1.53.0", + "postcss": "^8.5.6", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", "svelte": "^5.0.0", "svelte-check": "^4.0.0", "svelte-geolocation": "^1.0.0", - "tailwindcss": "^4.0.0", + "tailwindcss": "^4.1.18", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", "vite": "^6.2.6", @@ -89,6 +93,12 @@ "node": ">=6.9.0" } }, + "node_modules/@davepagurek/bezier-path": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@davepagurek/bezier-path/-/bezier-path-0.0.2.tgz", + "integrity": "sha512-4L9ddgzZc9DRGyl1RrS3z5nwnVJoyjsAelVG4X1jh4tVxryEHr4H9QavhxW/my6Rn3669Qz6mhv8gd5O/WeFTA==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", @@ -778,6 +788,12 @@ "node": ">=18.0.0" } }, + "node_modules/@japont/unicode-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@japont/unicode-range/-/unicode-range-1.0.0.tgz", + "integrity": "sha512-BckHvA2XdjRBVAWe2uceNuRf78lBeI28kyWEbfr/Q2pE17POkwuZ6WWY/UMv8FL9iBxhW4xfDoNLM9UVZaTeUQ==", + "license": "MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -1195,6 +1211,7 @@ "integrity": "sha512-DJm0UxVgzXq+1MUfiJK4Ridk7oIQsIets6JwHiEl97sI6nXScfXe+BeqNhzB7jQIVBb3BM51U4hNk8qQxRXBAA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", @@ -1228,6 +1245,7 @@ "integrity": "sha512-wojIS/7GYnJDYIg1higWj2ROA6sSRWvcR1PO/bqEyFr/5UZah26c8Cz4u0NaqjPeVltzsVpt2Tm8d2io0V+4Tw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", @@ -1278,6 +1296,13 @@ "tailwindcss": "4.1.10" } }, + "node_modules/@tailwindcss/node/node_modules/tailwindcss": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", + "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tailwindcss/oxide": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.10.tgz", @@ -1539,12 +1564,20 @@ "vite": "^5.2.0 || ^6" } }, + "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", + "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -1907,6 +1940,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/p5": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.7.7.tgz", + "integrity": "sha512-WFuP7jqc5CkkMtCK/NphgvMnJz1Qi9CMuK7t6xLu/tuXkRdGQA4q4AD0dUYcChC0Oibe8PE8gbKSFPNF0BqVNw==", + "license": "MIT", + "peer": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.34.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz", @@ -1953,6 +1993,7 @@ "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.34.1", "@typescript-eslint/types": "8.34.1", @@ -2183,6 +2224,7 @@ "integrity": "sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.6.1", @@ -2332,8 +2374,8 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2351,6 +2393,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2434,6 +2488,43 @@ "node": ">=18.2.0" } }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2451,6 +2542,16 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2475,6 +2576,41 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2495,6 +2631,27 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001761", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chai": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", @@ -2595,6 +2752,12 @@ "dev": true, "license": "MIT" }, + "node_modules/colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2719,6 +2882,13 @@ "dev": true, "license": "MIT" }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -2781,6 +2951,16 @@ "@esbuild/win32-x64": "0.25.5" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2794,12 +2974,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "9.29.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -2960,6 +3162,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3000,7 +3215,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -3020,7 +3234,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -3125,6 +3338,12 @@ "node": ">=16.0.0" } }, + "node_modules/file-saver": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", + "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3183,6 +3402,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -3198,6 +3431,12 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gifenc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gifenc/-/gifenc-1.0.3.tgz", + "integrity": "sha512-xdr6AdrfGBcfzncONUOlXMBuc5wJDtOueE3c5rdG0oNgtINLD+f2iFZltrBRZYzACRbKr+mSVU/x98zv2u3jmw==", + "license": "MIT" + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3248,6 +3487,24 @@ "node": ">=8" } }, + "node_modules/i18next": { + "version": "19.9.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.9.2.tgz", + "integrity": "sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.3.1.tgz", + "integrity": "sha512-KIToAzf8zwWvacgnRwJp63ase26o24AuNUlfNVJ5YZAFmdGhsJpmFClxXPuk9rv1FMI4lnc8zLSqgZPEZMrW4g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3427,6 +3684,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libtess": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/libtess/-/libtess-1.2.2.tgz", + "integrity": "sha512-Nps8HPeVVcsmJxUvFLKVJcCgcz+1ajPTXDVAVPs6+giOQP4AHV31uZFFkh+CKow/bkB7GbZWKmwmit7myaqDSw==", + "license": "SGI-B-2.0" + }, "node_modules/lightningcss": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", @@ -3875,6 +4138,19 @@ "dev": true, "license": "MIT" }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "license": "MIT" + }, "node_modules/openmeteo": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/openmeteo/-/openmeteo-1.2.0.tgz", @@ -3939,6 +4215,54 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/p5/-/p5-2.1.2.tgz", + "integrity": "sha512-M96K3FSwd31Sawsl9TzJ8kBZFuUy06eqRHpWw0DIQAOmYOosVyPr7Eh5GR2LsScba0X2wSnfRBYMz64oVQVOdg==", + "license": "LGPL-2.1", + "dependencies": { + "@davepagurek/bezier-path": "^0.0.2", + "@japont/unicode-range": "^1.0.0", + "acorn": "^8.12.1", + "acorn-walk": "^8.3.4", + "colorjs.io": "^0.5.2", + "escodegen": "^2.1.0", + "file-saver": "^1.3.8", + "gifenc": "^1.0.3", + "i18next": "^19.0.2", + "i18next-browser-languagedetector": "^4.0.1", + "libtess": "^1.2.2", + "omggif": "^1.0.10", + "pako": "^2.1.0", + "pixelmatch": "^7.1.0", + "zod": "^3.25.51" + } + }, + "node_modules/p5-svelte": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/p5-svelte/-/p5-svelte-3.1.2.tgz", + "integrity": "sha512-lcfWh+cJ1/wRdIXHnjpYmDgj2h3TCy1QJVQnf/cBcFWS8CSkvyAN5F8u8H2U8qBUtZ4XaD3nd+1NoYUMHaMExQ==", + "license": "MIT", + "dependencies": { + "p5": "^1.4.1" + }, + "peerDependencies": { + "@types/p5": "^1.4.2", + "p5": "^1.4.0" + } + }, + "node_modules/p5-svelte/node_modules/p5": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/p5/-/p5-1.11.11.tgz", + "integrity": "sha512-k58mfexvavFb+KNRpi70PbkKE2gCNiWQkzS4kVOyC2F9SKGgYy1jSO+JXZ24ikXV9OvZIAxGusiSVWEijYrmNg==", + "license": "LGPL-2.1" + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4002,6 +4326,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4009,12 +4334,25 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pixelmatch": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz", + "integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==", + "license": "ISC", + "dependencies": { + "pngjs": "^7.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, "node_modules/playwright": { "version": "1.53.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.1.tgz", "integrity": "sha512-LJ13YLr/ocweuwxyGf1XNFWIU4M2zUSo149Qbp+A4cpwDjsxRPj7k6H25LBrEHiEwxvRbD8HdwvQmRMSvquhYw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright-core": "1.53.1" }, @@ -4041,6 +4379,15 @@ "node": ">=18" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -4061,6 +4408,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -4178,6 +4526,13 @@ "node": ">=4" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4194,6 +4549,7 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4210,6 +4566,7 @@ "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" @@ -4537,6 +4894,16 @@ "node": ">=18" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4624,6 +4991,7 @@ "integrity": "sha512-5PEg+QQKce4t1qiOtVUhUS3AQRTtxJyGBTpxLcNWnr0Ve8q4r06bMo0Gv8uhtCPWlztZHoi3Ye7elLhu+PCTMg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -4715,9 +5083,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", - "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "dev": true, "license": "MIT" }, @@ -4912,6 +5280,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4943,6 +5312,37 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4980,6 +5380,7 @@ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -5125,6 +5526,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -5285,21 +5687,6 @@ "node": ">=18" } }, - "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -5319,6 +5706,15 @@ "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", "dev": true, "license": "MIT" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 181db28..1c66027 100644 --- a/package.json +++ b/package.json @@ -24,19 +24,21 @@ "@tailwindcss/vite": "^4.0.0", "@vitejs/plugin-basic-ssl": "^2.0.0", "@vitest/browser": "^3.2.3", + "autoprefixer": "^10.4.23", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", "eslint-plugin-svelte": "^3.0.0", "globals": "^16.0.0", "openmeteo": "^1.2.0", "playwright": "^1.53.0", + "postcss": "^8.5.6", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", "svelte": "^5.0.0", "svelte-check": "^4.0.0", "svelte-geolocation": "^1.0.0", - "tailwindcss": "^4.0.0", + "tailwindcss": "^4.1.18", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", "vite": "^6.2.6", @@ -45,6 +47,8 @@ "vitest-browser-svelte": "^0.1.0" }, "dependencies": { + "p5": "^2.1.2", + "p5-svelte": "^3.1.2", "tonal": "^6.4.2", "tone": "^15.1.22" } diff --git a/src/app.css b/src/app.css index 19335d4..2cacea5 100644 --- a/src/app.css +++ b/src/app.css @@ -1,7 +1,11 @@ -@import 'tailwindcss'; +@import "tailwindcss"; -.syne-mono-regular { - font-family: "Syne Mono", monospace; - font-weight: 400; - font-style: normal; +@theme { + --font-family-syne-mono: "Syne Mono", monospace; +} + +body { + background-color: black; + color: white; + font-family: var(--font-family-syne-mono); } \ No newline at end of file diff --git a/src/app.html b/src/app.html index 13b5694..420dee5 100644 --- a/src/app.html +++ b/src/app.html @@ -11,7 +11,7 @@ %sveltekit.head% - +
%sveltekit.body%
diff --git a/src/lib/audio/audio-effects.ts b/src/lib/audio/audio-effects.ts new file mode 100644 index 0000000..f21af0f --- /dev/null +++ b/src/lib/audio/audio-effects.ts @@ -0,0 +1,35 @@ +import * as Tone from 'tone'; + +export function createReverb(wetValue: number): Tone.Reverb { + const reverb = new Tone.Reverb({ + decay: 16, + preDelay: 0.5 + }); + reverb.wet.value = wetValue; + return reverb; +} + +export function createDelay(delayTime: string, feedback: number): Tone.FeedbackDelay { + const delay = new Tone.FeedbackDelay({ + delayTime: delayTime, + feedback: feedback + }); + delay.wet.value = 0.5; + return delay; +} + +export function createFilter(frequency: number, resonance: number): Tone.Filter { + return new Tone.Filter({ + type: 'lowpass', + frequency: frequency, + Q: resonance + }); +} + +export function createGain(volume: number): Tone.Gain { + return new Tone.Gain(Tone.dbToGain(volume)); +} + +export function createAnalyser(): Tone.Analyser { + return new Tone.Analyser('fft', 512); +} diff --git a/src/lib/audio/chord-progressions.ts b/src/lib/audio/chord-progressions.ts new file mode 100644 index 0000000..45c0c24 --- /dev/null +++ b/src/lib/audio/chord-progressions.ts @@ -0,0 +1,61 @@ +export type ChordProgression = Array<{ + time: string; + notes: string[]; +}>; + +// Bright, uplifting - for pleasant daytime conditions +export const brightProgression: ChordProgression = [ + { time: '0:0:0', notes: ['C4', 'E4', 'G4', 'B4'] }, // Cmaj7 + { time: '0:1:0', notes: ['F4', 'A4', 'C5', 'E5'] }, // Fmaj7 + { time: '0:2:0', notes: ['G4', 'B4', 'D5', 'F5'] }, // G7 + { time: '0:3:0', notes: ['A3', 'C4', 'E4', 'G4'] } // Am7 +]; + +// Dreamy, calm - for pleasant nighttime conditions +export const dreamyProgression: ChordProgression = [ + { time: '0:0:0', notes: ['D4', 'F#4', 'A4', 'C5'] }, // Dmaj7 + { time: '0:1:0', notes: ['G3', 'B3', 'D4', 'F#4'] }, // Gmaj7 + { time: '0:2:0', notes: ['E4', 'G4', 'B4', 'D5'] }, // Em7 + { time: '0:3:0', notes: ['A3', 'C#4', 'E4', 'G4'] } // A7 +]; + +// Melancholic, introspective - for cold/rainy conditions +export const melancholicProgression: ChordProgression = [ + { time: '0:0:0', notes: ['A3', 'C4', 'E4', 'G4'] }, // Am7 + { time: '0:1:0', notes: ['D3', 'F3', 'A3', 'C4'] }, // Dm7 + { time: '0:2:0', notes: ['G3', 'Bb3', 'D4', 'F4'] }, // Gm7 + { time: '0:3:0', notes: ['C3', 'E3', 'G3', 'Bb3'] } // C7 +]; + +// Tense, atmospheric - for stormy/extreme conditions +export const tenseProgression: ChordProgression = [ + { time: '0:0:0', notes: ['E3', 'G3', 'Bb3', 'D4'] }, // Em7b5 + { time: '0:1:0', notes: ['F3', 'Ab3', 'C4', 'Eb4'] }, // Fm7 + { time: '0:2:0', notes: ['Bb3', 'Db4', 'F4', 'Ab4'] }, // Bbm7 + { time: '0:3:0', notes: ['Eb3', 'Gb3', 'Bb3', 'Db4'] } // Ebm7 +]; + +// Warm, intense - for hot conditions +export const warmProgression: ChordProgression = [ + { time: '0:0:0', notes: ['E4', 'G#4', 'B4', 'D5'] }, // E7 + { time: '0:1:0', notes: ['A3', 'C#4', 'E4', 'G4'] }, // A7 + { time: '0:2:0', notes: ['D4', 'F#4', 'A4', 'C5'] }, // D7 + { time: '0:3:0', notes: ['G3', 'B3', 'D4', 'F4'] } // G7 +]; + +// Ethereal, floating - for foggy/misty conditions +export const etherealProgression: ChordProgression = [ + { time: '0:0:0', notes: ['F4', 'A4', 'C5', 'E5'] }, // Fmaj7 + { time: '0:1:0', notes: ['C4', 'E4', 'G4', 'B4'] }, // Cmaj7 + { time: '0:2:0', notes: ['G4', 'B4', 'D5', 'F#5'] }, // Gmaj7 + { time: '0:3:0', notes: ['D4', 'F#4', 'A4', 'C#5'] } // Dmaj7 +]; + +export const allProgressions = [ + brightProgression, + dreamyProgression, + melancholicProgression, + tenseProgression, + warmProgression, + etherealProgression +]; diff --git a/src/lib/audio/instruments/arpSynth.ts b/src/lib/audio/instruments/arpSynth.ts new file mode 100644 index 0000000..95d19ff --- /dev/null +++ b/src/lib/audio/instruments/arpSynth.ts @@ -0,0 +1,16 @@ +import * as Tone from 'tone'; + +export function createArpSynth(volume: number): Tone.Synth { + return new Tone.Synth({ + oscillator: { + type: 'triangle' + }, + envelope: { + attack: 0.005, + decay: 0.2, + sustain: 0, + release: 0.3 + }, + volume: volume + }); +} diff --git a/src/lib/audio/instruments/bassSynth.ts b/src/lib/audio/instruments/bassSynth.ts new file mode 100644 index 0000000..289971e --- /dev/null +++ b/src/lib/audio/instruments/bassSynth.ts @@ -0,0 +1,16 @@ +import * as Tone from 'tone'; + +export function createBassSynth(): Tone.Synth { + return new Tone.Synth({ + oscillator: { + type: 'sine' + }, + envelope: { + attack: 0.1, + decay: 0.3, + sustain: 0.8, + release: 2.0 + }, + volume: -20 + }); +} diff --git a/src/lib/audio/instruments/noiseSynth.ts b/src/lib/audio/instruments/noiseSynth.ts new file mode 100644 index 0000000..3326f68 --- /dev/null +++ b/src/lib/audio/instruments/noiseSynth.ts @@ -0,0 +1,16 @@ +import * as Tone from 'tone'; + +export function createNoiseSynth(volume: number): Tone.NoiseSynth { + return new Tone.NoiseSynth({ + noise: { + type: 'pink' + }, + envelope: { + attack: 0.005, + decay: 0.1, + sustain: 0, + release: 0.1 + }, + volume: volume + }); +} diff --git a/src/lib/audio/instruments/padSynth.ts b/src/lib/audio/instruments/padSynth.ts new file mode 100644 index 0000000..38a5060 --- /dev/null +++ b/src/lib/audio/instruments/padSynth.ts @@ -0,0 +1,16 @@ +import * as Tone from 'tone'; + +export function createPadSynth(isDay: boolean): Tone.PolySynth { + return new Tone.PolySynth(Tone.Synth, { + oscillator: { + type: isDay ? 'triangle' : 'sine' + }, + envelope: { + attack: 1.5, + decay: 1, + sustain: 0.7, + release: 1.0 + }, + volume: -20 + }); +} diff --git a/src/lib/audio/instruments/pingSynth.ts b/src/lib/audio/instruments/pingSynth.ts new file mode 100644 index 0000000..1c708ca --- /dev/null +++ b/src/lib/audio/instruments/pingSynth.ts @@ -0,0 +1,21 @@ +import * as Tone from 'tone'; + +/** + * Creates a sharp, high-pitched ping synth for extreme weather indicators + * @param volume - Volume in dB + * @returns A configured Synth instance + */ +export function createPingSynth(volume: number): Tone.Synth { + return new Tone.Synth({ + oscillator: { + type: 'triangle' + }, + envelope: { + attack: 0.001, // Very sharp attack + decay: 0.05, + sustain: 0, + release: 0.1 + }, + volume: volume + }); +} diff --git a/src/lib/audio/weather-mood.ts b/src/lib/audio/weather-mood.ts new file mode 100644 index 0000000..bf915e3 --- /dev/null +++ b/src/lib/audio/weather-mood.ts @@ -0,0 +1,97 @@ +import { + brightProgression, + dreamyProgression, + melancholicProgression, + tenseProgression, + warmProgression, + etherealProgression, + type ChordProgression +} from './chord-progressions'; + +interface WeatherConditions { + temperature2m: number; + relativeHumidity2m: number; + cloudCover: number; + windSpeed10m: number; + precipitation: number; + isDay: boolean; +} + +/** + * Calculate a weather mood score based on various conditions + * Returns values between 0 (harsh/extreme) and 1 (pleasant) + */ +export function calculateComfortScore(conditions: WeatherConditions): number { + const { temperature2m, relativeHumidity2m, cloudCover, windSpeed10m, precipitation } = + conditions; + + // Temperature comfort: ideal 15-25°C, drops off outside this range + let tempScore = 1.0; + if (temperature2m < 15) { + tempScore = Math.max(0, 1 - Math.abs(15 - temperature2m) / 30); + } else if (temperature2m > 25) { + tempScore = Math.max(0, 1 - Math.abs(temperature2m - 25) / 20); + } + + // Humidity comfort: ideal 40-60%, drops off outside + let humidityScore = 1.0; + if (relativeHumidity2m < 40) { + humidityScore = Math.max(0, relativeHumidity2m / 40); + } else if (relativeHumidity2m > 60) { + humidityScore = Math.max(0, 1 - (relativeHumidity2m - 60) / 40); + } + + // Cloud cover: some clouds (30-70%) is pleasant, extremes less so + const cloudScore = 1 - Math.abs(cloudCover - 50) / 50; + + // Wind: light breeze (0-15 km/h) is nice, strong wind less so + const windScore = Math.max(0, 1 - windSpeed10m / 40); + + // Precipitation: any is somewhat unpleasant + const precipScore = Math.max(0, 1 - precipitation / 10); + + // Weighted average + return ( + tempScore * 0.35 + + humidityScore * 0.2 + + cloudScore * 0.15 + + windScore * 0.15 + + precipScore * 0.15 + ); +} + +/** + * Select the appropriate chord progression based on weather conditions + */ +export function selectChordProgression(conditions: WeatherConditions): ChordProgression { + const comfortScore = calculateComfortScore(conditions); + const { temperature2m, precipitation, cloudCover, isDay } = conditions; + + // Stormy/extreme conditions (heavy rain, very harsh) + if (precipitation > 5 || comfortScore < 0.2) { + return tenseProgression; + } + + // Very hot conditions + if (temperature2m > 30) { + return warmProgression; + } + + // Cold/rainy/gloomy conditions + if (temperature2m < 5 || (precipitation > 1 && cloudCover > 70)) { + return melancholicProgression; + } + + // Foggy/misty conditions (high humidity + clouds, low wind) + if (conditions.relativeHumidity2m > 80 && cloudCover > 60 && temperature2m > 5) { + return etherealProgression; + } + + // Pleasant conditions - choose based on day/night + if (comfortScore > 0.6) { + return isDay ? brightProgression : dreamyProgression; + } + + // Default: slightly unpleasant but not extreme + return isDay ? dreamyProgression : melancholicProgression; +} diff --git a/src/lib/components/AudioVisualization.svelte b/src/lib/components/AudioVisualization.svelte new file mode 100644 index 0000000..45c860f --- /dev/null +++ b/src/lib/components/AudioVisualization.svelte @@ -0,0 +1,196 @@ + + +
+ {#if isPlaying} + + {:else} +
+

Press Play

+
+ {/if} +
+ + diff --git a/src/lib/generators/air-quality/AirQualityGen.svelte b/src/lib/generators/air-quality/AirQualityGen.svelte index 8bcc860..a8aeb9c 100644 --- a/src/lib/generators/air-quality/AirQualityGen.svelte +++ b/src/lib/generators/air-quality/AirQualityGen.svelte @@ -1,3 +1,238 @@ - - - + + +
+
+ + + {#if isPlaying} +
+

PM2.5: {pm25.toFixed(1)} µg/m³

+

PM10: {pm10.toFixed(1)} µg/m³

+

Dust: {dust.toFixed(1)} µg/m³

+

---

+

Pollution Index: {airQualityIndex.toFixed(1)}

+

Burst Interval: {burstInterval.toFixed(2)}s

+
+ {/if} +
+ +
+ +
+
diff --git a/src/lib/generators/weather/WeatherGen.svelte b/src/lib/generators/weather/WeatherGen.svelte index 375ce46..1df1555 100644 --- a/src/lib/generators/weather/WeatherGen.svelte +++ b/src/lib/generators/weather/WeatherGen.svelte @@ -1,238 +1,466 @@ +
+
+ - - -
- -
- -
+ {#if isPlaying} +
+

Temperature: {temperature2m.toFixed(1)}°C

+

Humidity: {relativeHumidity2m}%

+

Cloud Cover: {cloudCover}%

+

Wind Speed: {windSpeed10m.toFixed(1)} m/s

+

---

+

BPM: {bpm}

+

Weather Extremity: {(weatherExtremity * 100).toFixed(0)}%

+

Reverb: {reverbWet.toFixed(2)}

+

Delay: {delayTime} @ {delayFeedback.toFixed(2)} feedback

+

Filter: {Math.round(filterCutoff)}Hz Q:{filterResonance.toFixed(1)}

+
+ {/if} +
+ +
+ +
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 6b02058..a37bf60 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -9,9 +9,9 @@ let error: GeolocationError | undefined = $state(undefined); let options = { - enableHighAccuracy: true, - timeout: 5000, // milliseconds - maximumAge: 60 * 60 * 1000, // milliseconds + enableHighAccuracy: false, // Use WiFi/network location (faster than GPS) + timeout: 10000, // milliseconds - increased for better reliability + maximumAge: 0, // Don't use cached position }; $inspect(error); @@ -27,18 +27,18 @@
{#if !getPosition} - {:else if loading}

Loading...

{:else if error} -

We can't seem to find you.

+

We can't seem to find you.

{:else} -

+

Your Position is set as: {position?.coords?.latitude}, {position?.coords?.longitude}

- {/if} diff --git a/src/routes/on-out/+page.svelte b/src/routes/on-out/+page.svelte index 8eb92b7..301e4b6 100644 --- a/src/routes/on-out/+page.svelte +++ b/src/routes/on-out/+page.svelte @@ -1,5 +1,6 @@ - -
- +
+
+ + +
- diff --git a/src/routes/on-out/+page.ts b/src/routes/on-out/+page.ts index bc5e7c3..6bfb94a 100644 --- a/src/routes/on-out/+page.ts +++ b/src/routes/on-out/+page.ts @@ -13,7 +13,7 @@ export const load: PageLoad = async ({ url }) => { }; -const getWeather = async (long: string, lat: string): Promise => { +const getWeather = async (lat: string, long: string): Promise => { const apiParams = { "latitude": lat, "longitude": long, @@ -45,7 +45,7 @@ const getWeather = async (long: string, lat: string): Promise => { return weatherData } -const getAirQuality = async (long: string, lat: string): Promise => { +const getAirQuality = async (lat: string, long: string): Promise => { const params = { "latitude": lat, "longitude": long,