Initial commit

This commit is contained in:
Florian Sylvain
2023-04-12 12:18:18 +02:00
commit 2d21a06262
40 changed files with 1803 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
dist
node_modules
+6
View File
@@ -0,0 +1,6 @@
{
"tabWidth": 4,
"useTabs": true,
"semi": false,
"printWidth": 90
}
+619
View File
@@ -0,0 +1,619 @@
{
"name": "threejs-audio-starter",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "threejs-audio-starter",
"version": "0.0.0",
"dependencies": {
"lil-gui": "^0.17.0",
"three": "^0.148.0"
},
"devDependencies": {
"vite": "^4.0.4"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz",
"integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz",
"integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz",
"integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz",
"integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz",
"integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz",
"integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz",
"integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz",
"integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz",
"integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz",
"integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz",
"integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz",
"integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz",
"integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz",
"integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz",
"integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz",
"integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz",
"integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz",
"integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz",
"integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz",
"integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz",
"integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz",
"integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild": {
"version": "0.17.15",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz",
"integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.17.15",
"@esbuild/android-arm64": "0.17.15",
"@esbuild/android-x64": "0.17.15",
"@esbuild/darwin-arm64": "0.17.15",
"@esbuild/darwin-x64": "0.17.15",
"@esbuild/freebsd-arm64": "0.17.15",
"@esbuild/freebsd-x64": "0.17.15",
"@esbuild/linux-arm": "0.17.15",
"@esbuild/linux-arm64": "0.17.15",
"@esbuild/linux-ia32": "0.17.15",
"@esbuild/linux-loong64": "0.17.15",
"@esbuild/linux-mips64el": "0.17.15",
"@esbuild/linux-ppc64": "0.17.15",
"@esbuild/linux-riscv64": "0.17.15",
"@esbuild/linux-s390x": "0.17.15",
"@esbuild/linux-x64": "0.17.15",
"@esbuild/netbsd-x64": "0.17.15",
"@esbuild/openbsd-x64": "0.17.15",
"@esbuild/sunos-x64": "0.17.15",
"@esbuild/win32-arm64": "0.17.15",
"@esbuild/win32-ia32": "0.17.15",
"@esbuild/win32-x64": "0.17.15"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/lil-gui": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz",
"integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ=="
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/postcss": {
"version": "8.4.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/resolve": {
"version": "1.22.2",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
"dev": true,
"dependencies": {
"is-core-module": "^2.11.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "3.20.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz",
"integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/three": {
"version": "0.148.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.148.0.tgz",
"integrity": "sha512-8uzVV+qhTPi0bOFs/3te3RW6hb3urL8jYEl6irjCWo/l6sr8MPNMcClFev/MMYeIxr0gmDcoXTy/8LXh/LXkfw=="
},
"node_modules/vite": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
"integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==",
"dev": true,
"dependencies": {
"esbuild": "^0.17.5",
"postcss": "^8.4.21",
"resolve": "^1.22.1",
"rollup": "^3.18.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"@types/node": ">= 14",
"less": "*",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"name": "threejs-audio-starter",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"vite": "^4.0.4"
},
"dependencies": {
"lil-gui": "^0.17.0",
"three": "^0.148.0"
}
}
+14
View File
@@ -0,0 +1,14 @@
## Setup
Download [Node.js](https://nodejs.org/en/download/).
Run this followed commands:
``` bash
# Install dependencies (only the first time)
npm install
# Run the local server at localhost:8080
npm run dev
# Build for production in the dist/ directory
npm run build
```
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+41
View File
@@ -0,0 +1,41 @@
import * as THREE from "three"
import Experience from "./Experience.js"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
export default class Camera {
constructor() {
this.experience = new Experience()
this.sizes = this.experience.sizes
this.scene = this.experience.scene
this.canvas = this.experience.canvas
this.setInstance()
// this.setControls()
}
setInstance() {
this.instance = new THREE.PerspectiveCamera(
35,
this.sizes.width / this.sizes.height,
0.1,
100
)
this.instance.position.set(0, 0, 20)
this.scene.add(this.instance)
}
setControls() {
this.controls = new OrbitControls(this.instance, this.canvas)
this.controls.enableDamping = true
}
resize() {
this.instance.aspect = this.sizes.width / this.sizes.height
this.instance.updateProjectionMatrix()
}
update() {
// this.controls.update()
}
}
+93
View File
@@ -0,0 +1,93 @@
import * as THREE from "three"
import Debug from "./Utils/Debug.js"
import Sizes from "./Utils/Sizes.js"
import Time from "./Utils/Time.js"
import Camera from "./Camera.js"
import Renderer from "./Renderer.js"
import World from "./World/World.js"
import Resources from "./Utils/Resources.js"
import AudioAnalyser from "./Utils/AudioAnalyser.js"
import Interface from "./Interface.js"
import sources from "./sources.js"
let instance = null
export default class Experience {
constructor(_canvas) {
// Singleton
if (instance) {
return instance
}
instance = this
// Global access
window.experience = this
// Options
this.canvas = _canvas
// Setup
this.debug = new Debug()
this.sizes = new Sizes()
this.time = new Time()
this.scene = new THREE.Scene()
this.resources = new Resources(sources)
this.interface = new Interface()
this.camera = new Camera()
this.renderer = new Renderer()
this.world = new World()
this.audio = new AudioAnalyser("audio/onemoretime.mp3")
// Resize event
this.sizes.on("resize", () => {
this.resize()
})
// Time tick event
this.time.on("tick", () => {
this.update()
})
}
resize() {
this.camera.resize()
this.renderer.resize()
}
update() {
this.camera.update()
this.world.update()
this.renderer.update()
this.audio.update()
}
destroy() {
this.sizes.off("resize")
this.time.off("tick")
// Traverse the whole scene
this.scene.traverse((child) => {
// Test if it's a mesh
if (child instanceof THREE.Mesh) {
child.geometry.dispose()
// Loop through the material properties
for (const key in child.material) {
const value = child.material[key]
// Test if there is a dispose function
if (value && typeof value.dispose === "function") {
value.dispose()
}
}
}
})
this.camera.controls.dispose()
this.renderer.instance.dispose()
if (this.debug.active) this.debug.ui.destroy()
}
}
+27
View File
@@ -0,0 +1,27 @@
import EventEmitter from './Utils/EventEmitter.js'
import Experience from './Experience.js'
export default class Interface extends EventEmitter
{
constructor()
{
super()
this.experience = new Experience()
this.resources = this.experience.resources
this.$loader = document.querySelector('.loader')
this.$startBtn = document.querySelector('.start-btn')
// Resources loaded
this.resources.on('ready', () => {
this.$loader.classList.remove('is-active')
this.$startBtn.classList.add('is-active')
this.$startBtn.addEventListener('click', () => {
this.trigger('startExperience')
this.$startBtn.classList.remove('is-active')
})
})
}
}
+44
View File
@@ -0,0 +1,44 @@
import * as THREE from 'three'
import Experience from './Experience.js'
export default class Renderer
{
constructor()
{
this.experience = new Experience()
this.canvas = this.experience.canvas
this.sizes = this.experience.sizes
this.scene = this.experience.scene
this.camera = this.experience.camera
this.setInstance()
}
setInstance()
{
this.instance = new THREE.WebGLRenderer({
canvas: this.canvas,
antialias: true
})
this.instance.physicallyCorrectLights = true
this.instance.outputEncoding = THREE.sRGBEncoding
this.instance.toneMapping = THREE.CineonToneMapping
this.instance.toneMappingExposure = 1.75
this.instance.shadowMap.enabled = true
this.instance.shadowMap.type = THREE.PCFSoftShadowMap
this.instance.setClearColor('#ffffff')
this.instance.setSize(this.sizes.width, this.sizes.height)
this.instance.setPixelRatio(Math.min(this.sizes.pixelRatio, 2))
}
resize()
{
this.instance.setSize(this.sizes.width, this.sizes.height)
this.instance.setPixelRatio(Math.min(this.sizes.pixelRatio, 2))
}
update()
{
this.instance.render(this.scene, this.camera.instance)
}
}
BIN
View File
Binary file not shown.
+119
View File
@@ -0,0 +1,119 @@
import EventEmitter from "./EventEmitter.js"
import Experience from "../Experience.js"
export default class AudioAnalyser extends EventEmitter {
constructor(audioPath, fftSize = 1024) {
super()
this.experience = new Experience()
this.scene = this.experience.scene
this.resources = this.experience.resources
this.interface = this.experience.interface
this.averageFrequencies = 0
this.averageLowFrequencies = 0
this.averageMidFrequencies = 0
this.averageHighFrequencies = 0
this.interface.on("startExperience", () => {
this.audioContext = new AudioContext()
this.analyser = this.audioContext.createAnalyser()
this.analyser.fftSize = fftSize
this.frequencyData = new Uint8Array(this.analyser.frequencyBinCount)
this.timeData = new Uint8Array(this.analyser.fftSize)
this.isPlaying = false
this.gainNode = this.audioContext.createGain()
this.gainNode.gain.value = 0.1
this.audio = new Audio(audioPath)
this.source = this.audioContext.createMediaElementSource(this.audio)
this.source.connect(this.analyser)
this.source.connect(this.gainNode)
this.gainNode.connect(this.audioContext.destination)
this.play()
})
}
play() {
if (this.isPlaying) return
this.isPlaying = true
this.audio.play()
}
pause() {
if (!this.isPlaying) return
this.isPlaying = false
this.audio.pause()
}
update() {
if (!this.isPlaying) return
this.analyser.getByteFrequencyData(this.frequencyData)
this.analyser.getByteTimeDomainData(this.timeData)
this.setAverageLowFrequencies()
this.setAverageMediumFrequencies()
this.setAverageHighFrequencies()
this.setAverageFrequencies()
}
setVolume(gain) {
this.gainNode.gain.value = gain
}
setAverageFrequencies() {
// on add tous les éléments de l'array entre eux
const total = this.frequencyData.reduce((acc, cur) => acc + cur)
// on fait la moyenne des fréquences
const average = total / this.frequencyData.length
this.averageFrequencies = average / 255
}
setAverageLowFrequencies() {
// On divise l'array de toutes les frequences par 3 et on prend la première partie (les fréquences les plus hautes)
const third = Math.round(this.frequencyData.length / 3)
const lowFrequencies = this.frequencyData.slice(0, third)
// on add tous les éléments de l'array entre eux
const total = lowFrequencies.reduce((acc, cur) => acc + cur)
// on fait la moyenne des fréquences
const average = total / lowFrequencies.length
this.averageLowFrequencies = average / 255
}
setAverageMediumFrequencies() {
// On divise l'array de toutes les frequences par 3 et on prend la deuxieme partie (les fréquences mid)
const third = Math.round(this.frequencyData.length / 3)
const mediumFrequencies = this.frequencyData.slice(third, third * 2)
// on add tous les éléments de l'array entre eux
const total = mediumFrequencies.reduce((acc, cur) => acc + cur)
// on fait la moyenne des fréquences
const average = total / mediumFrequencies.length
this.averageMidFrequencies = average / 255
}
setAverageHighFrequencies() {
// On divise l'array de toutes les frequences par 3 et on prend la troiseme partie (les fréquences hautes)
const third = Math.round(this.frequencyData.length / 3)
const highFrequencies = this.frequencyData.slice(
third * 2,
this.frequencyData.length - 1
)
// on add tous les éléments de l'array entre eux
const total = highFrequencies.reduce((acc, cur) => acc + cur)
// on fait la moyenne des fréquences
const average = total / highFrequencies.length
this.averageHighFrequencies = average / 255
}
}
+14
View File
@@ -0,0 +1,14 @@
import * as dat from 'lil-gui'
export default class Debug
{
constructor()
{
this.active = window.location.hash === '#debug'
if(this.active)
{
this.ui = new dat.GUI()
}
}
}
+197
View File
@@ -0,0 +1,197 @@
export default class EventEmitter
{
constructor()
{
this.callbacks = {}
this.callbacks.base = {}
}
on(_names, callback)
{
// Errors
if(typeof _names === 'undefined' || _names === '')
{
console.warn('wrong names')
return false
}
if(typeof callback === 'undefined')
{
console.warn('wrong callback')
return false
}
// Resolve names
const names = this.resolveNames(_names)
// Each name
names.forEach((_name) =>
{
// Resolve name
const name = this.resolveName(_name)
// Create namespace if not exist
if(!(this.callbacks[ name.namespace ] instanceof Object))
this.callbacks[ name.namespace ] = {}
// Create callback if not exist
if(!(this.callbacks[ name.namespace ][ name.value ] instanceof Array))
this.callbacks[ name.namespace ][ name.value ] = []
// Add callback
this.callbacks[ name.namespace ][ name.value ].push(callback)
})
return this
}
off(_names)
{
// Errors
if(typeof _names === 'undefined' || _names === '')
{
console.warn('wrong name')
return false
}
// Resolve names
const names = this.resolveNames(_names)
// Each name
names.forEach((_name) =>
{
// Resolve name
const name = this.resolveName(_name)
// Remove namespace
if(name.namespace !== 'base' && name.value === '')
{
delete this.callbacks[ name.namespace ]
}
// Remove specific callback in namespace
else
{
// Default
if(name.namespace === 'base')
{
// Try to remove from each namespace
for(const namespace in this.callbacks)
{
if(this.callbacks[ namespace ] instanceof Object && this.callbacks[ namespace ][ name.value ] instanceof Array)
{
delete this.callbacks[ namespace ][ name.value ]
// Remove namespace if empty
if(Object.keys(this.callbacks[ namespace ]).length === 0)
delete this.callbacks[ namespace ]
}
}
}
// Specified namespace
else if(this.callbacks[ name.namespace ] instanceof Object && this.callbacks[ name.namespace ][ name.value ] instanceof Array)
{
delete this.callbacks[ name.namespace ][ name.value ]
// Remove namespace if empty
if(Object.keys(this.callbacks[ name.namespace ]).length === 0)
delete this.callbacks[ name.namespace ]
}
}
})
return this
}
trigger(_name, _args)
{
// Errors
if(typeof _name === 'undefined' || _name === '')
{
console.warn('wrong name')
return false
}
let finalResult = null
let result = null
// Default args
const args = !(_args instanceof Array) ? [] : _args
// Resolve names (should on have one event)
let name = this.resolveNames(_name)
// Resolve name
name = this.resolveName(name[ 0 ])
// Default namespace
if(name.namespace === 'base')
{
// Try to find callback in each namespace
for(const namespace in this.callbacks)
{
if(this.callbacks[ namespace ] instanceof Object && this.callbacks[ namespace ][ name.value ] instanceof Array)
{
this.callbacks[ namespace ][ name.value ].forEach(function(callback)
{
result = callback.apply(this, args)
if(typeof finalResult === 'undefined')
{
finalResult = result
}
})
}
}
}
// Specified namespace
else if(this.callbacks[ name.namespace ] instanceof Object)
{
if(name.value === '')
{
console.warn('wrong name')
return this
}
this.callbacks[ name.namespace ][ name.value ].forEach(function(callback)
{
result = callback.apply(this, args)
if(typeof finalResult === 'undefined')
finalResult = result
})
}
return finalResult
}
resolveNames(_names)
{
let names = _names
names = names.replace(/[^a-zA-Z0-9 ,/.]/g, '')
names = names.replace(/[,/]+/g, ' ')
names = names.split(' ')
return names
}
resolveName(name)
{
const newName = {}
const parts = name.split('.')
newName.original = name
newName.value = parts[ 0 ]
newName.namespace = 'base' // Base namespace
// Specified namespace
if(parts.length > 1 && parts[ 1 ] !== '')
{
newName.namespace = parts[ 1 ]
}
return newName
}
}
+89
View File
@@ -0,0 +1,89 @@
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import EventEmitter from './EventEmitter.js'
export default class Resources extends EventEmitter
{
constructor(sources)
{
super()
this.sources = sources
this.items = {}
this.toLoad = this.sources.length
this.loaded = 0
this.setLoaders()
this.startLoading()
}
setLoaders()
{
this.loaders = {}
this.loaders.audioLoader = new THREE.AudioLoader()
this.loaders.gltfLoader = new GLTFLoader()
this.loaders.textureLoader = new THREE.TextureLoader()
this.loaders.cubeTextureLoader = new THREE.CubeTextureLoader()
}
startLoading()
{
// Load each source
for(const source of this.sources)
{
if(source.type === 'gltfModel')
{
this.loaders.gltfLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
)
}
else if (source.type === 'audio')
{
this.loaders.audioLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
)
}
else if(source.type === 'texture')
{
this.loaders.textureLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
)
}
else if(source.type === 'cubeTexture')
{
this.loaders.cubeTextureLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
)
}
}
}
sourceLoaded(source, file)
{
this.items[source.name] = file
this.loaded++
if(this.loaded === this.toLoad)
{
this.trigger('ready')
}
}
}
+24
View File
@@ -0,0 +1,24 @@
import EventEmitter from './EventEmitter.js'
export default class Sizes extends EventEmitter
{
constructor()
{
super()
// Setup
this.width = window.innerWidth
this.height = window.innerHeight
this.pixelRatio = Math.min(window.devicePixelRatio, 2)
// Resize event
window.addEventListener('resize', () =>
{
this.width = window.innerWidth
this.height = window.innerHeight
this.pixelRatio = Math.min(window.devicePixelRatio, 2)
this.trigger('resize')
})
}
}
+35
View File
@@ -0,0 +1,35 @@
import EventEmitter from './EventEmitter.js'
export default class Time extends EventEmitter
{
constructor()
{
super()
// Setup
this.start = Date.now()
this.current = this.start
this.elapsed = 0
this.delta = 16
window.requestAnimationFrame(() =>
{
this.tick()
})
}
tick()
{
const currentTime = Date.now()
this.delta = currentTime - this.current
this.current = currentTime
this.elapsed = this.current - this.start
this.trigger('tick')
window.requestAnimationFrame(() =>
{
this.tick()
})
}
}
BIN
View File
Binary file not shown.
+71
View File
@@ -0,0 +1,71 @@
import * as THREE from "three"
import Experience from "../Experience.js"
export default class Cube {
constructor(position) {
this.experience = new Experience()
this.scene = this.experience.scene
this.initialPosition = {
x: position.x,
y: position.y,
z: position.z,
}
this.setGeometry()
this.setMaterial()
this.setMesh()
this.rotation = {
speed: {
x: Math.random() * 2 - 1,
y: Math.random() * 2 - 1,
z: Math.random() * 2 - 1,
},
}
this.randomDirection = {
x: Math.random() * 2 - 1,
y: Math.random() * 2 - 1,
}
}
setGeometry() {
this.geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2)
}
setMaterial() {
this.material = new THREE.MeshStandardMaterial({
color: new THREE.Color(
Math.round(Math.random()),
Math.round(Math.random()),
Math.round(Math.random())
),
})
}
setMesh() {
this.mesh = new THREE.Mesh(this.geometry, this.material)
this.mesh.position.x = this.initialPosition.x
this.mesh.position.y = this.initialPosition.y
this.mesh.position.z = this.initialPosition.z
this.mesh.receiveShadow = true
this.mesh.castShadow = true
this.scene.add(this.mesh)
}
update() {
this.mesh.rotation.x += this.rotation.speed.x
this.mesh.rotation.y += this.rotation.speed.y
this.mesh.rotation.z += this.rotation.speed.z
this.mesh.position.x += this.randomDirection.x * 0.1
this.mesh.position.y += this.randomDirection.y * 0.1
}
destroy() {
this.scene.remove(this.mesh)
}
}
+94
View File
@@ -0,0 +1,94 @@
import * as THREE from "three"
import Experience from "../Experience.js"
export default class Environment {
constructor() {
this.experience = new Experience()
this.scene = this.experience.scene
this.resources = this.experience.resources
this.debug = this.experience.debug
// Debug
if (this.debug.active) {
this.debugFolder = this.debug.ui.addFolder("environment")
}
this.setSunLight()
this.setEnvironmentMap()
}
setSunLight() {
this.sunLight = new THREE.DirectionalLight("#ffffff", 4)
this.sunLight.castShadow = true
this.sunLight.shadow.camera.far = 15
this.sunLight.shadow.mapSize.set(1024, 1024)
this.sunLight.shadow.normalBias = 0.05
this.sunLight.position.set(3.5, 2, -1.25)
this.scene.add(this.sunLight)
// Debug
if (this.debug.active) {
this.debugFolder
.add(this.sunLight, "intensity")
.name("sunLightIntensity")
.min(0)
.max(10)
.step(0.001)
this.debugFolder
.add(this.sunLight.position, "x")
.name("sunLightX")
.min(-5)
.max(5)
.step(0.001)
this.debugFolder
.add(this.sunLight.position, "y")
.name("sunLightY")
.min(-5)
.max(5)
.step(0.001)
this.debugFolder
.add(this.sunLight.position, "z")
.name("sunLightZ")
.min(-5)
.max(5)
.step(0.001)
}
}
setEnvironmentMap() {
this.environmentMap = {}
this.environmentMap.intensity = 0.4
this.environmentMap.texture = this.resources.items.environmentMapTexture
this.environmentMap.texture.encoding = THREE.sRGBEncoding
this.scene.environment = this.environmentMap.texture
this.environmentMap.updateMaterials = () => {
this.scene.traverse((child) => {
if (
child instanceof THREE.Mesh &&
child.material instanceof THREE.MeshStandardMaterial
) {
child.material.envMap = this.environmentMap.texture
child.material.envMapIntensity = this.environmentMap.intensity
child.material.needsUpdate = true
}
})
}
this.environmentMap.updateMaterials()
// Debug
if (this.debug.active) {
this.debugFolder
.add(this.environmentMap, "intensity")
.name("envMapIntensity")
.min(0)
.max(4)
.step(0.001)
.onChange(this.environmentMap.updateMaterials)
}
}
}
+31
View File
@@ -0,0 +1,31 @@
import * as THREE from "three"
import Experience from "../Experience.js"
export default class Sphere {
constructor() {
this.experience = new Experience()
this.scene = this.experience.scene
this.resources = this.experience.resources
this.debug = this.experience.debug
this.setSphere()
}
setSphere() {
this.sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.1, 16, 16),
new THREE.MeshStandardMaterial({
color: "#ffffff",
})
)
this.sphere.receiveShadow = true
this.sphere.castShadow = true
this.scene.add(this.sphere)
}
update() {}
destroy() {
this.scene.remove(this.sphere)
}
}
+154
View File
@@ -0,0 +1,154 @@
import * as THREE from "three"
import Experience from "../Experience.js"
import Environment from "./Environment.js"
import Cube from "./Cube.js"
export default class World {
constructor() {
this.experience = new Experience()
this.scene = this.experience.scene
this.interface = this.experience.interface
this.debug = this.experience.debug
this.mouseTimeout = undefined
this.cubes = []
this.colorsPick = {}
this.marioSprite = undefined
this.downVolTimout = undefined
this.upVolTimout = undefined
this.interface.on("startExperience", () => {
this.environment = new Environment()
this.scene.background = new THREE.Color(0x000000)
this.updateColorPick()
this.initMario()
document
.querySelector("body")
.addEventListener("mousemove", (e) => this.onMouseMove(e))
document
.querySelector("body")
.addEventListener("touchmove", (e) => this.onMouseMove(e))
})
}
initMario() {
const spriteTexture = new THREE.TextureLoader().load(
`${import.meta.env.BASE_URL}/textures/mario.png`
)
const spriteMaterial = new THREE.SpriteMaterial({ map: spriteTexture })
this.marioSprite = new THREE.Sprite(spriteMaterial)
this.marioSprite.position.set(0, 0, 0)
this.scene.add(this.marioSprite)
}
onMouseMove(event) {
if (!this.experience.audio) return
const mousePosition = this.getMousePosition(event)
this.upVolume()
this.spawnEphemeralCubes(mousePosition, 200)
this.scene.background = new THREE.Color(this.getBackgroundRandomColor())
this.marioSprite.position.y =
Math.sin(this.experience.time.elapsed * 0.02) * 0.5 + 0.5
if (this.mouseTimeout) clearTimeout(this.mouseTimeout)
this.mouseTimeout = setTimeout(() => this.onMouseStop(), 50)
}
onMouseStop() {
this.downVolume()
this.scene.background = new THREE.Color(0x000000)
this.marioSprite.position.y = 0
}
updateColorPick() {
this.colorsPick = {
r: Math.round(Math.random()),
g: Math.round(Math.random()),
b: Math.round(Math.random()),
}
setTimeout(() => this.updateColorPick(), 1000)
}
getBackgroundRandomColor() {
return new THREE.Color(
this.colorsPick.r * this.experience.audio.averageMidFrequencies,
this.colorsPick.g * this.experience.audio.averageMidFrequencies,
this.colorsPick.b * this.experience.audio.averageMidFrequencies
)
}
upVolume() {
if (this.experience.audio.gainNode.gain.value >= 0.8) return
clearTimeout(this.downVolTimout)
this.setVolume(
Math.round((this.experience.audio.gainNode.gain.value + 0.1) * 10) / 10
)
this.upVolTimout = setTimeout(() => {
this.upVolume()
}, 200)
}
downVolume() {
if (this.experience.audio.gainNode.gain.value <= 0.2) return
clearTimeout(this.upVolTimout)
this.setVolume(
Math.round((this.experience.audio.gainNode.gain.value - 0.1) * 10) / 10
)
this.downVolTimout = setTimeout(() => {
this.downVolume()
}, 200)
}
setVolume(volume) {
this.experience.audio.gainNode.gain.value = volume
}
getMousePosition(event) {
const client = event.touches ? event.touches[0] : event
const raycaster = new THREE.Raycaster()
const mouse = {
x: (client.clientX / window.innerWidth) * 2 - 1,
y: -(client.clientY / window.innerHeight) * 2 + 1,
z: 0,
}
raycaster.setFromCamera(mouse, window.experience.camera.instance)
return {
x: raycaster.ray.direction.x * 20,
y: raycaster.ray.direction.y * 20,
z: 0,
}
}
spawnEphemeralCubes(position, quantity) {
if (this.cubes.length > quantity) {
this.cubes[0].destroy()
this.cubes = this.cubes.slice(1)
}
const cube = new Cube(position)
this.cubes.push(cube)
setTimeout(() => {
cube.destroy()
this.cubes = this.cubes.filter((c) => c !== cube)
}, 1000)
}
update() {
if (!this.environment) return
this.cubes.forEach((cube) => {
cube.update()
})
}
}
+20
View File
@@ -0,0 +1,20 @@
export default [
{
name: 'environmentMapTexture',
type: 'cubeTexture',
path:
[
'textures/environmentMap/px.jpg',
'textures/environmentMap/nx.jpg',
'textures/environmentMap/py.jpg',
'textures/environmentMap/ny.jpg',
'textures/environmentMap/pz.jpg',
'textures/environmentMap/nz.jpg'
]
},
{
name: 'music',
type: 'audio',
path: 'audio/onemoretime.mp3'
}
]
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audio experience</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<p class="loader is-active">Loading...</p>
<p class="start-btn">Start experience</p>
<canvas class="webgl"></canvas>
<script type="module" src="./script.js"></script>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
import Experience from "./Experience/Experience.js"
export const experience = new Experience(document.querySelector("canvas.webgl"))
+49
View File
@@ -0,0 +1,49 @@
*
{
margin: 0;
padding: 0;
}
html,
body
{
overflow: hidden;
}
.webgl
{
position: fixed;
top: 0;
left: 0;
outline: none;
}
.loader {
z-index: 3;
cursor: pointer;
position: absolute;
top: 50%;
left: 50%;
display: none;
transform: translate(-50%, -50%);
text-transform: uppercase;
}
.loader.is-active {
display: block;
}
.start-btn {
z-index: 2;
cursor: pointer;
position: absolute;
top: 50%;
left: 50%;
display: none;
transform: translate(-50%, -50%);
text-transform: uppercase;
}
.start-btn.is-active {
display: block;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+25
View File
@@ -0,0 +1,25 @@
import { defineConfig } from "vite"
const isCodeSandbox = "SANDBOX_URL" in process.env || "CODESANDBOX_HOST" in process.env
export default defineConfig(({ command }) => {
const baseUrl =
command === "serve" ? "https://projet.floriansylvain.fr/Creative_Coding" : ""
return {
define: {
BASE_URL: JSON.stringify(baseUrl),
},
root: "src/",
publicDir: "../static/",
base: "/Creative_Coding/",
server: {
host: true,
open: !isCodeSandbox, // Open if it's not a CodeSandbox
},
build: {
outDir: "../dist",
emptyOutDir: true,
},
}
})