├── .gitignore ├── LICENSE ├── README.md ├── Social.webp ├── assets ├── BoinkTheatreState.json ├── TheatreTutorial_1.theatre-project-state.json ├── fonts │ ├── Sassy_Frass.zip │ └── Sassy_Frass │ │ ├── OFL.txt │ │ └── SassyFrass-Regular.ttf ├── images │ └── swoosh.png └── sounds │ ├── boing-2-44164.mp3 │ ├── boing-6222.mp3 │ ├── boink.mp3 │ ├── jews_harp_boing-7111.mp3 │ ├── little-whoosh-2-6301.mp3 │ ├── loud-thud-45719.mp3 │ └── whoosh.mp3 ├── favicon.ico ├── favicon.png ├── index.html ├── package.json ├── src ├── main.ts ├── style.css └── vite-env.d.ts ├── thebenezerHex.ico ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dev 3 | build 4 | /package-lock.json 5 | .vscode 6 | .DS_Store 7 | src/.DS_Store 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ebenezer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Theatre.js Beginner Tutorial 2 | 3 | In this tutorial we will learn how to animate a bouncy cube using theatre.js and three.js 4 | 5 | Screenshot 2023-03-01 at 09 12 49 6 | 7 | [Live Demo](https://bouncy.vercel.app/) 8 | 9 | [Article on Codrops](https://tympanus.net/codrops/2023/03/30/mastering-theatre-js-animations-learn-to-create-dynamic-visuals/) 10 | 11 | [Youtube Video](https://youtu.be/wT7P6qjAg30) 12 | 13 | ## Setup 14 | Download [Node.js](https://nodejs.org/en/download/). 15 | Run this followed commands: 16 | 17 | ## Scripts 18 | - Install dependencies (only the first time) 19 | ``` bash 20 | yarn install 21 | ``` 22 | 23 | - Start the dev server: 24 | 25 | ``` bash 26 | yarn run dev 27 | ``` 28 | 29 | - Build for production: 30 | 31 | ``` bash 32 | yarn run build 33 | ``` 34 | 35 | ## Misc 36 | 37 | Follow Me: [Twitter](https://twitter.com/th_ebenezer), [Youtube](https://www.youtube.com/@thebenezer), [GitHub](https://github.com/thebenezer) 38 | 39 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/) 40 | 41 | ## License 42 | [MIT](LICENSE) 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Social.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/Social.webp -------------------------------------------------------------------------------- /assets/BoinkTheatreState.json: -------------------------------------------------------------------------------- 1 | { 2 | "sheetsById": { 3 | "AnimationScene": { 4 | "staticOverrides": { 5 | "byObject": { 6 | "Box": { 7 | "rotation": { 8 | "x": 0.30381249072837857, 9 | "y": 0.15190624536418929, 10 | "z": -0.45571873609256786, 11 | "xR": 0, 12 | "yR": 0, 13 | "zR": 0 14 | }, 15 | "position": { 16 | "y": 5, 17 | "x": 0 18 | } 19 | }, 20 | "Scene": { 21 | "back": { 22 | "r": 0.2901960784313726, 23 | "g": 0.6705882352941176, 24 | "b": 0.6705882352941176, 25 | "a": 1 26 | }, 27 | "backgroundColor": { 28 | "r": 0.4627450980392157, 29 | "g": 0.8666666666666667, 30 | "b": 0.8666666666666667, 31 | "a": 1 32 | }, 33 | "autoRotate": false 34 | }, 35 | "Colors": { 36 | "backgroundColor": { 37 | "r": 0.5803921568627451, 38 | "g": 0.6745098039215687, 39 | "b": 0.6901960784313725, 40 | "a": 1 41 | }, 42 | "floorColor": { 43 | "r": 0.7176470588235294, 44 | "g": 0.8156862745098039, 45 | "b": 0.7058823529411765, 46 | "a": 1 47 | }, 48 | "boxColor": { 49 | "r": 0.5058823529411764, 50 | "g": 0.8980392156862745, 51 | "b": 0.8862745098039215, 52 | "a": 1 53 | } 54 | }, 55 | "Effects": { 56 | "boxGlow": { 57 | "r": 0, 58 | "g": 0.1450980392156863, 59 | "b": 0.20784313725490197, 60 | "a": 1 61 | }, 62 | "swooshScale": 1 63 | }, 64 | "text": { 65 | "scale": 35, 66 | "opacity": 1 67 | } 68 | } 69 | }, 70 | "sequence": { 71 | "subUnitsPerUnit": 30, 72 | "length": 2.03, 73 | "type": "PositionalSequence", 74 | "tracksByObject": { 75 | "Box": { 76 | "trackData": { 77 | "c7JZ_4VDOd": { 78 | "type": "BasicKeyframedTrack", 79 | "__debugName": "Box:[\"position\",\"y\"]", 80 | "keyframes": [ 81 | { 82 | "id": "AG4KVSKLwq", 83 | "position": 0, 84 | "connectedRight": true, 85 | "handles": [ 86 | 0.5, 87 | 1, 88 | 0.5, 89 | 0 90 | ], 91 | "type": "bezier", 92 | "value": 40 93 | }, 94 | { 95 | "id": "eZORIMKoq7", 96 | "position": 0.8, 97 | "connectedRight": true, 98 | "handles": [ 99 | 0.9250359712230216, 100 | 0.5472972972972974, 101 | 0.04460431654676264, 102 | 0.4729729729729729 103 | ], 104 | "type": "bezier", 105 | "value": 5 106 | }, 107 | { 108 | "id": "pLSeAFshKX", 109 | "position": 1, 110 | "connectedRight": true, 111 | "handles": [ 112 | 0.5, 113 | 1, 114 | 0.5, 115 | 0 116 | ], 117 | "type": "bezier", 118 | "value": 0.7 119 | }, 120 | { 121 | "id": "D1riNX5Q-i", 122 | "position": 1.2, 123 | "connectedRight": true, 124 | "handles": [ 125 | 0.9250359712230216, 126 | 0.5472972972972974, 127 | 0.04460431654676264, 128 | 0.4729729729729729 129 | ], 130 | "type": "bezier", 131 | "value": 9.362374254053337 132 | }, 133 | { 134 | "id": "CBbjcndi9X", 135 | "position": 2, 136 | "connectedRight": true, 137 | "handles": [ 138 | 0.5, 139 | 1, 140 | 0.5, 141 | 0 142 | ], 143 | "type": "bezier", 144 | "value": 40 145 | } 146 | ] 147 | }, 148 | "NnzZqnI-eW": { 149 | "type": "BasicKeyframedTrack", 150 | "__debugName": "Box:[\"scale\",\"xS\"]", 151 | "keyframes": [ 152 | { 153 | "id": "zymIAEgZV6", 154 | "position": 0.8, 155 | "connectedRight": true, 156 | "handles": [ 157 | 0.5, 158 | 1, 159 | 0.5, 160 | 0 161 | ], 162 | "type": "bezier", 163 | "value": 1 164 | }, 165 | { 166 | "id": "go4FxwmVaI", 167 | "position": 1, 168 | "connectedRight": true, 169 | "handles": [ 170 | 0.5, 171 | 1, 172 | 0.5, 173 | 0 174 | ], 175 | "type": "bezier", 176 | "value": 2 177 | }, 178 | { 179 | "id": "QzCfUrDjvP", 180 | "position": 1.2, 181 | "connectedRight": true, 182 | "handles": [ 183 | 0.5, 184 | 1, 185 | 0.5, 186 | 0 187 | ], 188 | "type": "bezier", 189 | "value": 0.8 190 | }, 191 | { 192 | "id": "suBpdPWLoZ", 193 | "position": 2, 194 | "connectedRight": true, 195 | "handles": [ 196 | 0.5, 197 | 1, 198 | 0.5, 199 | 0 200 | ], 201 | "type": "bezier", 202 | "value": 1 203 | } 204 | ] 205 | }, 206 | "SXB6XGM_yr": { 207 | "type": "BasicKeyframedTrack", 208 | "__debugName": "Box:[\"scale\",\"yS\"]", 209 | "keyframes": [ 210 | { 211 | "id": "nzwqDqv_Aa", 212 | "position": 0, 213 | "connectedRight": true, 214 | "handles": [ 215 | 0.5, 216 | 1, 217 | 0.5, 218 | 0 219 | ], 220 | "type": "bezier", 221 | "value": 1 222 | }, 223 | { 224 | "id": "9y2DeHePdj", 225 | "position": 0.8, 226 | "connectedRight": true, 227 | "handles": [ 228 | 0.5, 229 | 1, 230 | 0.5, 231 | 0 232 | ], 233 | "type": "bezier", 234 | "value": 1 235 | }, 236 | { 237 | "id": "d8A3hwfF9p", 238 | "position": 1, 239 | "connectedRight": true, 240 | "handles": [ 241 | 0.5, 242 | 1, 243 | 0.03223954476234216, 244 | 0.6026170798898073 245 | ], 246 | "type": "bezier", 247 | "value": 0.20000000000000004 248 | }, 249 | { 250 | "id": "KTwr_8t_iC", 251 | "position": 1.2, 252 | "connectedRight": true, 253 | "handles": [ 254 | 0.5, 255 | 1, 256 | 1, 257 | 2.353394007695285 258 | ], 259 | "type": "bezier", 260 | "value": 2 261 | }, 262 | { 263 | "id": "uCcW8UtXvP", 264 | "position": 2, 265 | "connectedRight": true, 266 | "handles": [ 267 | 0.40268648676022895, 268 | 1.2039158015162899, 269 | 0.5, 270 | 0 271 | ], 272 | "type": "bezier", 273 | "value": 1 274 | } 275 | ] 276 | }, 277 | "0HOOcDqBBs": { 278 | "type": "BasicKeyframedTrack", 279 | "__debugName": "Box:[\"scale\",\"zS\"]", 280 | "keyframes": [ 281 | { 282 | "id": "LQpJP3ghTL", 283 | "position": 0.8, 284 | "connectedRight": true, 285 | "handles": [ 286 | 0.5, 287 | 1, 288 | 0.5, 289 | 0 290 | ], 291 | "type": "bezier", 292 | "value": 1 293 | }, 294 | { 295 | "id": "z43Q1sQ3Ty", 296 | "position": 1, 297 | "connectedRight": true, 298 | "handles": [ 299 | 0.5, 300 | 1, 301 | 0.5, 302 | 0 303 | ], 304 | "type": "bezier", 305 | "value": 2 306 | }, 307 | { 308 | "id": "mojrmmU4o5", 309 | "position": 1.2, 310 | "connectedRight": true, 311 | "handles": [ 312 | 0.5, 313 | 1, 314 | 0.5, 315 | 0 316 | ], 317 | "type": "bezier", 318 | "value": 0.8 319 | }, 320 | { 321 | "id": "YEhB9DnVtY", 322 | "position": 2, 323 | "connectedRight": true, 324 | "handles": [ 325 | 0.5, 326 | 1, 327 | 0.5, 328 | 0 329 | ], 330 | "type": "bezier", 331 | "value": 1 332 | } 333 | ] 334 | } 335 | }, 336 | "trackIdByPropPath": { 337 | "[\"position\",\"y\"]": "c7JZ_4VDOd", 338 | "[\"scale\",\"xS\"]": "NnzZqnI-eW", 339 | "[\"scale\",\"yS\"]": "SXB6XGM_yr", 340 | "[\"scale\",\"zS\"]": "0HOOcDqBBs" 341 | } 342 | }, 343 | "Effects": { 344 | "trackData": { 345 | "2FqY-lHqYf": { 346 | "type": "BasicKeyframedTrack", 347 | "__debugName": "Effects:[\"boxGlow\"]", 348 | "keyframes": [ 349 | { 350 | "id": "GXenBOMZPL", 351 | "position": 0, 352 | "connectedRight": true, 353 | "handles": [ 354 | 0.5, 355 | 1, 356 | 0.5, 357 | 0 358 | ], 359 | "type": "bezier", 360 | "value": { 361 | "r": 0, 362 | "g": 0, 363 | "b": 0, 364 | "a": 1 365 | } 366 | }, 367 | { 368 | "id": "G0mlhvwzDs", 369 | "position": 0.8, 370 | "connectedRight": true, 371 | "handles": [ 372 | 0.5, 373 | 1, 374 | 0.5, 375 | 0 376 | ], 377 | "type": "bezier", 378 | "value": { 379 | "r": 0, 380 | "g": 0, 381 | "b": 0, 382 | "a": 1 383 | } 384 | }, 385 | { 386 | "id": "xK4e_Lb6lv", 387 | "position": 1, 388 | "connectedRight": true, 389 | "handles": [ 390 | 0.5, 391 | 1, 392 | 0.5, 393 | 0 394 | ], 395 | "type": "bezier", 396 | "value": { 397 | "r": 0.4392156862745098, 398 | "g": 0, 399 | "b": 0, 400 | "a": 1 401 | } 402 | }, 403 | { 404 | "id": "JPBL-iRRLA", 405 | "position": 2, 406 | "connectedRight": true, 407 | "handles": [ 408 | 0.5, 409 | 1, 410 | 0.5, 411 | 0 412 | ], 413 | "type": "bezier", 414 | "value": { 415 | "r": 0, 416 | "g": 0, 417 | "b": 0, 418 | "a": 1 419 | } 420 | } 421 | ] 422 | }, 423 | "WAOgL5dRuk": { 424 | "type": "BasicKeyframedTrack", 425 | "__debugName": "Effects:[\"swooshPosition\"]", 426 | "keyframes": [ 427 | { 428 | "id": "PypdIR5nZn", 429 | "position": 0, 430 | "connectedRight": true, 431 | "handles": [ 432 | 0.5, 433 | 1, 434 | 0.5, 435 | 0 436 | ], 437 | "type": "bezier", 438 | "value": 49 439 | }, 440 | { 441 | "id": "LSAxxmbnUw", 442 | "position": 0.5, 443 | "connectedRight": true, 444 | "handles": [ 445 | 0.5, 446 | 1, 447 | 0.5, 448 | 0 449 | ], 450 | "type": "bezier", 451 | "value": 43.07000000000009 452 | }, 453 | { 454 | "id": "HppHANEHt1", 455 | "position": 0.8, 456 | "connectedRight": true, 457 | "handles": [ 458 | 0.5, 459 | 1, 460 | 0.5, 461 | 0 462 | ], 463 | "type": "bezier", 464 | "value": 23.450000000000152 465 | }, 466 | { 467 | "id": "DZu_3Y-kGQ", 468 | "position": 1, 469 | "connectedRight": true, 470 | "handles": [ 471 | 0.5, 472 | 1, 473 | 0.5, 474 | 0 475 | ], 476 | "type": "bezier", 477 | "value": 13.570000000000157 478 | } 479 | ] 480 | }, 481 | "VVMsoSNJvL": { 482 | "type": "BasicKeyframedTrack", 483 | "__debugName": "Effects:[\"swooshScale\"]", 484 | "keyframes": [ 485 | { 486 | "id": "Yg1-0OfYc5", 487 | "position": 0, 488 | "connectedRight": true, 489 | "handles": [ 490 | 0.5, 491 | 1, 492 | 0.5, 493 | 0 494 | ], 495 | "type": "bezier", 496 | "value": 0 497 | }, 498 | { 499 | "id": "Cv8FVI8QWa", 500 | "position": 0.5, 501 | "connectedRight": true, 502 | "handles": [ 503 | 0.5, 504 | 1, 505 | 0.5, 506 | 0 507 | ], 508 | "type": "bezier", 509 | "value": 1 510 | }, 511 | { 512 | "id": "hQnSUxBihu", 513 | "position": 1, 514 | "connectedRight": true, 515 | "handles": [ 516 | 0.5, 517 | 1, 518 | 0.5, 519 | 0 520 | ], 521 | "type": "bezier", 522 | "value": 0 523 | } 524 | ] 525 | } 526 | }, 527 | "trackIdByPropPath": { 528 | "[\"boxGlow\"]": "2FqY-lHqYf", 529 | "[\"swooshPosition\"]": "WAOgL5dRuk", 530 | "[\"swooshScale\"]": "VVMsoSNJvL" 531 | } 532 | }, 533 | "text": { 534 | "trackData": { 535 | "1VvziKw59T": { 536 | "type": "BasicKeyframedTrack", 537 | "__debugName": "text:[\"opacity\"]", 538 | "keyframes": [ 539 | { 540 | "id": "DmVMwhyJZc", 541 | "position": 0, 542 | "connectedRight": true, 543 | "handles": [ 544 | 0.5, 545 | 1, 546 | 0.5, 547 | 0 548 | ], 549 | "type": "bezier", 550 | "value": 0 551 | }, 552 | { 553 | "id": "47oQfGJfpJ", 554 | "position": 1.2, 555 | "connectedRight": true, 556 | "handles": [ 557 | 0.5, 558 | 1, 559 | 0.5, 560 | 0 561 | ], 562 | "type": "bezier", 563 | "value": 0 564 | }, 565 | { 566 | "id": "u8XjDpNOlC", 567 | "position": 1.267, 568 | "connectedRight": true, 569 | "handles": [ 570 | 0.5, 571 | 1, 572 | 0.5, 573 | 0 574 | ], 575 | "type": "bezier", 576 | "value": 1 577 | }, 578 | { 579 | "id": "ZrTvA4e56L", 580 | "position": 1.9, 581 | "connectedRight": true, 582 | "handles": [ 583 | 0.5, 584 | 1, 585 | 0.5, 586 | 0 587 | ], 588 | "type": "bezier", 589 | "value": 1 590 | }, 591 | { 592 | "id": "Fo5xAFF8Lf", 593 | "position": 2, 594 | "connectedRight": true, 595 | "handles": [ 596 | 0.5, 597 | 1, 598 | 0.5, 599 | 0 600 | ], 601 | "type": "bezier", 602 | "value": 0 603 | } 604 | ] 605 | }, 606 | "DuLRwtAaTk": { 607 | "type": "BasicKeyframedTrack", 608 | "__debugName": "text:[\"text\"]", 609 | "keyframes": [ 610 | { 611 | "id": "1YV7xwkGfL", 612 | "position": 1.4, 613 | "connectedRight": true, 614 | "handles": [ 615 | 0.5, 616 | 1, 617 | 0.5, 618 | 0 619 | ], 620 | "type": "bezier", 621 | "value": "Boink!!" 622 | } 623 | ] 624 | }, 625 | "nserN3nLJW": { 626 | "type": "BasicKeyframedTrack", 627 | "__debugName": "text:[\"scale\"]", 628 | "keyframes": [ 629 | { 630 | "id": "eE0RDDIc2_", 631 | "position": 1.267, 632 | "connectedRight": true, 633 | "handles": [ 634 | 0.5, 635 | 1, 636 | 0, 637 | 0.7155963302752294 638 | ], 639 | "type": "bezier", 640 | "value": 0 641 | }, 642 | { 643 | "id": "41m6ckT5SU", 644 | "position": 1.9, 645 | "connectedRight": true, 646 | "handles": [ 647 | 0.5, 648 | 1, 649 | 0.5, 650 | 0 651 | ], 652 | "type": "bezier", 653 | "value": 50 654 | } 655 | ] 656 | } 657 | }, 658 | "trackIdByPropPath": { 659 | "[\"opacity\"]": "1VvziKw59T", 660 | "[\"text\"]": "DuLRwtAaTk", 661 | "[\"scale\"]": "nserN3nLJW" 662 | } 663 | } 664 | } 665 | } 666 | } 667 | }, 668 | "definitionVersion": "0.4.0", 669 | "revisionHistory": [ 670 | "foEdTV0khAYeKGcp" 671 | ] 672 | } -------------------------------------------------------------------------------- /assets/TheatreTutorial_1.theatre-project-state.json: -------------------------------------------------------------------------------- 1 | { 2 | "sheetsById": { 3 | "AnimationScene": { 4 | "staticOverrides": { 5 | "byObject": { 6 | "Box": { 7 | "rotation": { 8 | "x": 0.30381249072837857, 9 | "y": 0.15190624536418929, 10 | "z": -0.45571873609256786, 11 | "xR": 0, 12 | "yR": 0, 13 | "zR": 0 14 | }, 15 | "position": { 16 | "y": 5, 17 | "x": 0 18 | } 19 | }, 20 | "Scene": { 21 | "back": { 22 | "r": 0.2901960784313726, 23 | "g": 0.6705882352941176, 24 | "b": 0.6705882352941176, 25 | "a": 1 26 | }, 27 | "backgroundColor": { 28 | "r": 0.1411764705882353, 29 | "g": 0.1607843137254902, 30 | "b": 0.1607843137254902, 31 | "a": 1 32 | }, 33 | "autoRotate": false 34 | } 35 | } 36 | }, 37 | "sequence": { 38 | "subUnitsPerUnit": 30, 39 | "length": 2.03, 40 | "type": "PositionalSequence", 41 | "tracksByObject": { 42 | "Box": { 43 | "trackData": { 44 | "c7JZ_4VDOd": { 45 | "type": "BasicKeyframedTrack", 46 | "__debugName": "Box:[\"position\",\"y\"]", 47 | "keyframes": [ 48 | { 49 | "id": "AG4KVSKLwq", 50 | "position": 0, 51 | "connectedRight": true, 52 | "handles": [ 53 | 0.5, 54 | 1, 55 | 0.5, 56 | 0 57 | ], 58 | "type": "bezier", 59 | "value": 40 60 | }, 61 | { 62 | "id": "eZORIMKoq7", 63 | "position": 0.8, 64 | "connectedRight": true, 65 | "handles": [ 66 | 0.9250359712230216, 67 | 0.5472972972972974, 68 | 0.04460431654676264, 69 | 0.4729729729729729 70 | ], 71 | "type": "bezier", 72 | "value": 5 73 | }, 74 | { 75 | "id": "pLSeAFshKX", 76 | "position": 1, 77 | "connectedRight": true, 78 | "handles": [ 79 | 0.5, 80 | 1, 81 | 0.5, 82 | 0 83 | ], 84 | "type": "bezier", 85 | "value": 0.7 86 | }, 87 | { 88 | "id": "D1riNX5Q-i", 89 | "position": 1.2, 90 | "connectedRight": true, 91 | "handles": [ 92 | 0.9250359712230216, 93 | 0.5472972972972974, 94 | 0.04460431654676264, 95 | 0.4729729729729729 96 | ], 97 | "type": "bezier", 98 | "value": 9.362374254053337 99 | }, 100 | { 101 | "id": "CBbjcndi9X", 102 | "position": 2, 103 | "connectedRight": true, 104 | "handles": [ 105 | 0.5, 106 | 1, 107 | 0.5, 108 | 0 109 | ], 110 | "type": "bezier", 111 | "value": 40 112 | } 113 | ] 114 | }, 115 | "NnzZqnI-eW": { 116 | "type": "BasicKeyframedTrack", 117 | "__debugName": "Box:[\"scale\",\"xS\"]", 118 | "keyframes": [ 119 | { 120 | "id": "zymIAEgZV6", 121 | "position": 0.8, 122 | "connectedRight": true, 123 | "handles": [ 124 | 0.5, 125 | 1, 126 | 0.5, 127 | 0 128 | ], 129 | "type": "bezier", 130 | "value": 1 131 | }, 132 | { 133 | "id": "go4FxwmVaI", 134 | "position": 1, 135 | "connectedRight": true, 136 | "handles": [ 137 | 0.5, 138 | 1, 139 | 0.5, 140 | 0 141 | ], 142 | "type": "bezier", 143 | "value": 2 144 | }, 145 | { 146 | "id": "QzCfUrDjvP", 147 | "position": 1.2, 148 | "connectedRight": true, 149 | "handles": [ 150 | 0.5, 151 | 1, 152 | 0.5, 153 | 0 154 | ], 155 | "type": "bezier", 156 | "value": 0.8 157 | }, 158 | { 159 | "id": "suBpdPWLoZ", 160 | "position": 2, 161 | "connectedRight": true, 162 | "handles": [ 163 | 0.5, 164 | 1, 165 | 0.5, 166 | 0 167 | ], 168 | "type": "bezier", 169 | "value": 1 170 | } 171 | ] 172 | }, 173 | "SXB6XGM_yr": { 174 | "type": "BasicKeyframedTrack", 175 | "__debugName": "Box:[\"scale\",\"yS\"]", 176 | "keyframes": [ 177 | { 178 | "id": "nzwqDqv_Aa", 179 | "position": 0, 180 | "connectedRight": true, 181 | "handles": [ 182 | 0.5, 183 | 1, 184 | 0.5, 185 | 0 186 | ], 187 | "type": "bezier", 188 | "value": 1 189 | }, 190 | { 191 | "id": "9y2DeHePdj", 192 | "position": 0.8, 193 | "connectedRight": true, 194 | "handles": [ 195 | 0.5, 196 | 1, 197 | 0.5, 198 | 0 199 | ], 200 | "type": "bezier", 201 | "value": 1 202 | }, 203 | { 204 | "id": "d8A3hwfF9p", 205 | "position": 1, 206 | "connectedRight": true, 207 | "handles": [ 208 | 0.5, 209 | 1, 210 | 0.03223954476234216, 211 | 0.6026170798898073 212 | ], 213 | "type": "bezier", 214 | "value": 0.20000000000000004 215 | }, 216 | { 217 | "id": "KTwr_8t_iC", 218 | "position": 1.2, 219 | "connectedRight": true, 220 | "handles": [ 221 | 0.5, 222 | 1, 223 | 1, 224 | 2.353394007695285 225 | ], 226 | "type": "bezier", 227 | "value": 2 228 | }, 229 | { 230 | "id": "uCcW8UtXvP", 231 | "position": 2, 232 | "connectedRight": true, 233 | "handles": [ 234 | 0.40268648676022895, 235 | 1.2039158015162899, 236 | 0.5, 237 | 0 238 | ], 239 | "type": "bezier", 240 | "value": 1 241 | } 242 | ] 243 | }, 244 | "0HOOcDqBBs": { 245 | "type": "BasicKeyframedTrack", 246 | "__debugName": "Box:[\"scale\",\"zS\"]", 247 | "keyframes": [ 248 | { 249 | "id": "LQpJP3ghTL", 250 | "position": 0.8, 251 | "connectedRight": true, 252 | "handles": [ 253 | 0.5, 254 | 1, 255 | 0.5, 256 | 0 257 | ], 258 | "type": "bezier", 259 | "value": 1 260 | }, 261 | { 262 | "id": "z43Q1sQ3Ty", 263 | "position": 1, 264 | "connectedRight": true, 265 | "handles": [ 266 | 0.5, 267 | 1, 268 | 0.5, 269 | 0 270 | ], 271 | "type": "bezier", 272 | "value": 2 273 | }, 274 | { 275 | "id": "mojrmmU4o5", 276 | "position": 1.2, 277 | "connectedRight": true, 278 | "handles": [ 279 | 0.5, 280 | 1, 281 | 0.5, 282 | 0 283 | ], 284 | "type": "bezier", 285 | "value": 0.8 286 | }, 287 | { 288 | "id": "YEhB9DnVtY", 289 | "position": 2, 290 | "connectedRight": true, 291 | "handles": [ 292 | 0.5, 293 | 1, 294 | 0.5, 295 | 0 296 | ], 297 | "type": "bezier", 298 | "value": 1 299 | } 300 | ] 301 | } 302 | }, 303 | "trackIdByPropPath": { 304 | "[\"position\",\"y\"]": "c7JZ_4VDOd", 305 | "[\"scale\",\"xS\"]": "NnzZqnI-eW", 306 | "[\"scale\",\"yS\"]": "SXB6XGM_yr", 307 | "[\"scale\",\"zS\"]": "0HOOcDqBBs" 308 | } 309 | } 310 | } 311 | } 312 | } 313 | }, 314 | "definitionVersion": "0.4.0", 315 | "revisionHistory": [ 316 | "kp8TgVzHvT-seqkV" 317 | ] 318 | } -------------------------------------------------------------------------------- /assets/fonts/Sassy_Frass.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/fonts/Sassy_Frass.zip -------------------------------------------------------------------------------- /assets/fonts/Sassy_Frass/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2008 The Sassy Frass Project Authors (https://github.com/googlefonts/sassy-frass) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/fonts/Sassy_Frass/SassyFrass-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/fonts/Sassy_Frass/SassyFrass-Regular.ttf -------------------------------------------------------------------------------- /assets/images/swoosh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/images/swoosh.png -------------------------------------------------------------------------------- /assets/sounds/boing-2-44164.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/boing-2-44164.mp3 -------------------------------------------------------------------------------- /assets/sounds/boing-6222.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/boing-6222.mp3 -------------------------------------------------------------------------------- /assets/sounds/boink.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/boink.mp3 -------------------------------------------------------------------------------- /assets/sounds/jews_harp_boing-7111.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/jews_harp_boing-7111.mp3 -------------------------------------------------------------------------------- /assets/sounds/little-whoosh-2-6301.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/little-whoosh-2-6301.mp3 -------------------------------------------------------------------------------- /assets/sounds/loud-thud-45719.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/loud-thud-45719.mp3 -------------------------------------------------------------------------------- /assets/sounds/whoosh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/assets/sounds/whoosh.mp3 -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/favicon.ico -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/favicon.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Theatre.js Tutorial 1 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |

Bouncy

33 |
Theatre.js Tutorial by thebenezer
34 | Watch Tutorial 35 | 47 |
48 |
49 |

Tap to start

50 |
51 |
52 |
53 | 56 | 59 |
60 |
61 | 64 | 67 |
68 |
69 |
Boink!!
70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "threejs-theatre-example-ts", 3 | "private": true, 4 | "version": "0.1.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "license": "MIT", 11 | "dependencies": { 12 | "@theatre/core": "^0.6.0", 13 | "@theatre/studio": "^0.6.0", 14 | "@types/three": "^0.141.0", 15 | "three": "^0.142.0", 16 | "typescript": "^4.5.4" 17 | }, 18 | "devDependencies": { 19 | "vite": "^2.9.9" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | import * as THREE from 'three'; 3 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; 4 | import {CSS2DRenderer,CSS2DObject} from 'three/examples/jsm/renderers/CSS2DRenderer' 5 | 6 | import { getProject, onChange, types, val } from '@theatre/core'; 7 | import projectState from '../assets/BoinkTheatreState.json'; 8 | import swooshSound from '../assets/sounds/whoosh.mp3'; 9 | import boinkSound from '../assets/sounds/boink.mp3'; 10 | import thudSound from '../assets/sounds/loud-thud-45719.mp3'; 11 | 12 | import studio from '@theatre/studio'; 13 | 14 | let project; 15 | if (import.meta.env.DEV) { 16 | studio.initialize(); 17 | // Create a project from local state 18 | project = getProject('TheatreTutorial_1'); 19 | } 20 | else { 21 | // Create a project from saved state 22 | project = getProject('TheatreTutorial_1', { state: projectState }); 23 | } 24 | 25 | // Create a sheet 26 | const sheet = project.sheet('AnimationScene'); 27 | 28 | let controls: OrbitControls; 29 | let camera: THREE.PerspectiveCamera; 30 | let renderer: THREE.WebGLRenderer; 31 | let textRenderer:CSS2DRenderer; 32 | let scene: THREE.Scene; 33 | const loadingMgr = new THREE.LoadingManager( 34 | //onLoad 35 | (()=>{ 36 | // Play sequence on click once all assets are loaded 37 | const tapStart = document.getElementById('tapStart'); 38 | // @ts-ignore 39 | tapStart.addEventListener( 40 | 'click', 41 | function () { 42 | soundReady = true; 43 | // @ts-ignore 44 | tapStart.style.opacity = "0"; 45 | setTimeout(()=>{ 46 | // @ts-ignore 47 | tapStart.style.display = "none"; 48 | },400) 49 | sheet.sequence.play({ iterationCount: Infinity, range: [0, 2] }); 50 | } 51 | ); 52 | }), 53 | //onProgress 54 | ((url: string, loaded: number, total: number)=>{ 55 | console.log(url,loaded,total) 56 | }), 57 | //onError 58 | ((url:string)=>{ 59 | console.log(url) 60 | }) 61 | ) 62 | 63 | 64 | const listener = new THREE.AudioListener(); 65 | const loader = new THREE.AudioLoader(loadingMgr); 66 | let soundReady = false; 67 | 68 | const swoosh = new THREE.Audio(listener) 69 | const boink = new THREE.Audio(listener) 70 | const thud = new THREE.Audio(listener) 71 | 72 | init(); 73 | requestAnimationFrame(tick); 74 | 75 | function init() { 76 | // Camera 77 | camera = new THREE.PerspectiveCamera( 78 | 70, 79 | window.innerWidth / window.innerHeight, 80 | 10, 81 | 300, 82 | ); 83 | camera.position.set(-70, 20, 70); 84 | 85 | // Scene 86 | scene = new THREE.Scene(); 87 | // scene.background = new THREE.Color(0x292929); 88 | 89 | // TextRenderer 90 | textRenderer = new CSS2DRenderer(); 91 | textRenderer.setSize(window.innerWidth,window.innerHeight); 92 | textRenderer.domElement.style.position = 'absolute'; 93 | textRenderer.domElement.style.top = "0"; 94 | textRenderer.domElement.style.left = "0"; 95 | textRenderer.domElement.style.width = "100%"; 96 | textRenderer.domElement.style.height = "100%"; 97 | textRenderer.domElement.style.zIndex = "2"; 98 | document.body.appendChild(textRenderer.domElement) 99 | 100 | // Renderer 101 | renderer = new THREE.WebGLRenderer({ antialias: true,alpha:true }); 102 | renderer.shadowMap.enabled = true; 103 | renderer.shadowMap.autoUpdate = true; 104 | renderer.shadowMap.type = THREE.PCFSoftShadowMap; 105 | renderer.outputEncoding = THREE.sRGBEncoding; 106 | renderer.toneMapping = THREE.LinearToneMapping; 107 | renderer.setSize(window.innerWidth, window.innerHeight); 108 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 109 | renderer.render(scene, camera); 110 | renderer.domElement.style.zIndex="1"; 111 | renderer.domElement.style.top = "0"; 112 | renderer.domElement.style.left = "0"; 113 | renderer.domElement.style.width = "100%"; 114 | renderer.domElement.style.height = "100%"; 115 | renderer.domElement.style.position="absolute"; 116 | document.body.appendChild(renderer.domElement); 117 | 118 | // Fog 119 | scene.fog = new THREE.FogExp2(0x94acb0, 0.009); 120 | 121 | setupLights(); 122 | setupOrbitControls(); 123 | setupEventListeners(); 124 | setupSounds(); 125 | 126 | // ***** setup our scene ******* 127 | 128 | // Box 129 | const geometry = new THREE.BoxGeometry(10, 10, 10); 130 | const boxMaterial = new THREE.MeshPhongMaterial({ color: 0x049ef4,emissive:0xff0000 }); 131 | 132 | const box = new THREE.Mesh(geometry, boxMaterial); 133 | box.castShadow = true; 134 | box.receiveShadow = true; 135 | scene.add(box); 136 | 137 | // Floor 138 | const floorGeometry = new THREE.CylinderGeometry(30, 30, 300, 30); 139 | const floorMaterial = new THREE.MeshPhongMaterial({ color: 0xf0f0f0 }); 140 | 141 | const floor = new THREE.Mesh(floorGeometry, floorMaterial); 142 | floor.position.set(0, -150, 0); 143 | floor.receiveShadow = true; 144 | scene.add(floor); 145 | 146 | // Swoosh Effect Objects 147 | const swooshMaterial = new THREE.MeshBasicMaterial({color:0x222222,transparent:true,opacity:1}); 148 | 149 | const swooshEffect = new THREE.Group(); 150 | 151 | const swooshBig = new THREE.Mesh(geometry, swooshMaterial ); 152 | swooshBig.scale.set(0.02,2,0.02) 153 | swooshBig.position.set(1,0,-2) 154 | 155 | const swooshSmall1 = new THREE.Mesh(geometry, swooshMaterial ); 156 | swooshSmall1.scale.set(0.02,1,0.02) 157 | swooshSmall1.position.set(1,0,3) 158 | 159 | const swooshSmall2 = new THREE.Mesh(geometry, swooshMaterial ); 160 | swooshSmall2.scale.set(0.02,1.4,0.02) 161 | swooshSmall2.position.set(-3,0,0) 162 | 163 | swooshEffect.add( swooshBig, swooshSmall1, swooshSmall2 ); 164 | swooshEffect.position.set(0,20,0) 165 | scene.add(swooshEffect) 166 | 167 | // Text Effects 168 | const boinkDom = document.getElementById('boink'); 169 | // @ts-ignore 170 | const boinkText = new CSS2DObject(boinkDom); 171 | boinkText.position.set(-25,0,0) 172 | box.add(boinkText); 173 | 174 | 175 | // ***** THEATRE CONFIG OBJECTS ******* 176 | 177 | const boxObj = sheet.object('Box', { 178 | rotation: types.compound({ 179 | xR: types.number(box.rotation.x, { range: [-Math.PI, Math.PI] }), 180 | yR: types.number(box.rotation.y, { range: [-Math.PI, Math.PI] }), 181 | zR: types.number(box.rotation.z, { range: [-Math.PI, Math.PI] }), 182 | }), 183 | position: types.compound({ 184 | x: types.number(0, { nudgeMultiplier: 0.1 }), 185 | y: types.number(0, { nudgeMultiplier: 0.1 }), 186 | z: types.number(0, { nudgeMultiplier: 0.1 }), 187 | }), 188 | scale: types.compound({ 189 | xS: types.number(1, { nudgeMultiplier: 0.1 }), 190 | yS: types.number(1, { nudgeMultiplier: 0.1 }), 191 | zS: types.number(1, { nudgeMultiplier: 0.1 }), 192 | }), 193 | }); 194 | 195 | const colorObj = sheet.object('Colors',{ 196 | backgroundColor: types.rgba(), 197 | floorColor: types.rgba(), 198 | boxColor: types.rgba(), 199 | }) 200 | 201 | const boxEffectsObj = sheet.object('Effects',{ 202 | boxGlow:types.rgba(), 203 | swooshScale:types.number(1,{nudgeMultiplier:0.01}), 204 | swooshPosition:types.number(0,{nudgeMultiplier:0.01}), 205 | swooshOpacity:types.number(1,{nudgeMultiplier:0.01}) 206 | }) 207 | 208 | const textEffectObj = sheet.object('text',{ 209 | opacity:1, 210 | text:"", 211 | scale: 1 212 | }); 213 | 214 | textEffectObj.onValuesChange((values)=>{ 215 | if(!boinkDom)return; 216 | boinkDom.innerText = values.text; 217 | boinkDom.style.opacity = ""+values.opacity 218 | boinkDom.style.fontSize = ""+values.scale+"px"; 219 | }) 220 | 221 | boxObj.onValuesChange((values) => { 222 | const { xR, yR, zR } = values.rotation; 223 | box.rotation.set(xR, yR, zR); 224 | const { x, y, z } = values.position; 225 | box.position.set(x, y, z); 226 | const { xS, yS, zS } = values.scale; 227 | box.scale.set(xS, yS, zS); 228 | }); 229 | 230 | colorObj.onValuesChange((values)=>{ 231 | // scene.background = new THREE.Color(values.backgroundColor.toString()); 232 | // @ts-ignore 233 | scene.fog.color = new THREE.Color(values.backgroundColor.toString()); 234 | // @ts-ignore 235 | document.body.style.backgroundColor = values.backgroundColor; 236 | floorMaterial.color.setRGB(values.floorColor.r,values.floorColor.g,values.floorColor.b) 237 | boxMaterial.color.setRGB(values.boxColor.r,values.boxColor.g,values.boxColor.b) 238 | }) 239 | 240 | boxEffectsObj.onValuesChange((values)=>{ 241 | boxMaterial.emissive.setRGB(values.boxGlow.r,values.boxGlow.g,values.boxGlow.b); 242 | swooshEffect.scale.setY(values.swooshScale); 243 | swooshEffect.position.setY(values.swooshPosition); 244 | swooshMaterial.opacity=values.swooshScale; 245 | }) 246 | 247 | // play the audio based on pointer position 248 | onChange(sheet.sequence.pointer.position, (position) => { 249 | if(!soundReady)return; 250 | if(position > 0.79 && position < 0.83){ 251 | if(!thud.isPlaying){ 252 | thud.play(); 253 | } 254 | } 255 | else if(position > 1.18 && position < 1.23){ 256 | if(!boink.isPlaying){ 257 | boink.play(); 258 | } 259 | } 260 | else if(position > 0.00 && position<0.04){ 261 | if(!swoosh.isPlaying){ 262 | swoosh.playbackRate= 1.7; 263 | swoosh.play(); 264 | } 265 | } 266 | }) 267 | 268 | } 269 | 270 | // RAF Update the screen 271 | function tick(): void { 272 | renderer.render(scene, camera); 273 | textRenderer.render(scene, camera); 274 | controls.update(); 275 | window.requestAnimationFrame(tick); 276 | } 277 | 278 | function setupSounds() { 279 | camera.add(listener); 280 | 281 | audioSetup(swoosh,swooshSound,0.3,loader) 282 | audioSetup(boink,boinkSound,0.2,loader) 283 | audioSetup(thud,thudSound,0.5,loader) 284 | } 285 | 286 | function audioSetup(sound:THREE.Audio, url:string,volume:number,loader:THREE.AudioLoader){ 287 | loader.load( 288 | url, 289 | // onLoad callback 290 | function ( audioBuffer ) { 291 | // set the audio object buffer to the loaded object 292 | sound.setBuffer( audioBuffer ); 293 | sound.setVolume(volume) 294 | sound.loop=false; 295 | }, 296 | ); 297 | } 298 | 299 | function setupLights() { 300 | // ***** Lights ****** // 301 | const ambLight = new THREE.AmbientLight(0xfefefe, 0.1); 302 | const rectLight = new THREE.DirectionalLight(0x00fff0, 0.6); 303 | rectLight.position.set(20, 30, -20); 304 | const dirLight = new THREE.DirectionalLight(0xfefefe, 1.5); 305 | dirLight.castShadow = true; 306 | dirLight.shadow.mapSize.width = 1024; 307 | dirLight.shadow.mapSize.height = 1024; 308 | dirLight.shadow.camera.far = 100; 309 | dirLight.shadow.camera.near = 1; 310 | dirLight.shadow.camera.top = 40; 311 | dirLight.shadow.camera.right = 40; 312 | dirLight.shadow.camera.bottom = -40; 313 | dirLight.shadow.camera.left = -40; 314 | 315 | dirLight.position.set(20, 30, 20); 316 | scene.add(ambLight, dirLight,rectLight); 317 | } 318 | 319 | function setupOrbitControls() { 320 | // OrbitControls 321 | controls = new OrbitControls(camera, textRenderer.domElement); 322 | controls.enableZoom = true; 323 | controls.enableRotate = true; 324 | controls.enableDamping = true; 325 | controls.autoRotate = false; 326 | controls.rotateSpeed = 1; 327 | controls.dampingFactor = 0.08; 328 | controls.minDistance = 30; 329 | controls.maxDistance = 120; 330 | controls.target.set(0, 20, 0); 331 | controls.maxPolarAngle = 4*(Math.PI / 7); 332 | controls.minPolarAngle = 1*(Math.PI / 7); 333 | } 334 | 335 | function setupEventListeners() { 336 | // Handle `resize` events 337 | window.addEventListener( 338 | 'resize', 339 | function () { 340 | camera.aspect = window.innerWidth / window.innerHeight; 341 | camera.updateProjectionMatrix(); 342 | 343 | renderer.setSize(window.innerWidth, window.innerHeight); 344 | renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 345 | textRenderer.setSize(window.innerWidth, window.innerHeight); 346 | }, 347 | false, 348 | ); 349 | 350 | 351 | const toggleSoundDom = document.getElementById('toggleSound'); 352 | // @ts-ignore 353 | toggleSoundDom.addEventListener('click',()=>{toggleSound()},false) 354 | 355 | const toggleAnimationDom = document.getElementById('toggleAnimation'); 356 | // @ts-ignore 357 | toggleAnimationDom.addEventListener('click',()=>{toggleAnimation()},false) 358 | 359 | } 360 | 361 | function toggleSound() { 362 | const soundOn = document.getElementById('soundOn'); 363 | const soundOff = document.getElementById('soundOff'); 364 | if (!soundOn || !soundOff) return; 365 | if (soundReady == false) { 366 | soundOn.style.display = 'block'; 367 | soundOff.style.display = 'none'; 368 | } else { 369 | soundOn.style.display = 'none'; 370 | soundOff.style.display = 'block'; 371 | } 372 | soundReady=!soundReady; 373 | } 374 | 375 | function toggleAnimation() { 376 | const play = document.getElementById('play'); 377 | const pause = document.getElementById('pause'); 378 | if (!play || !pause) return; 379 | if (!val(sheet.sequence.pointer.playing)) { 380 | play.style.display = 'none'; 381 | pause.style.display = 'block'; 382 | sheet.sequence.play({ iterationCount: Infinity, range: [0, 2] }); 383 | } else { 384 | play.style.display = 'block'; 385 | pause.style.display = 'none'; 386 | sheet.sequence.pause(); 387 | } 388 | } -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Sonsie+One&display=swap'); 2 | @import url('https://fonts.googleapis.com/css2?family=Kaushan+Script&family=Poppins:ital,wght@0,300;1,500&display=swap'); 3 | @font-face { 4 | font-family: 'Sassy Frass'; 5 | src: url('../assets/fonts/Sassy_Frass/SassyFrass-Regular.ttf'); 6 | } 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | } 13 | 14 | html, 15 | body { 16 | overflow: hidden; 17 | font-family: 'Sonsie One', cursive; 18 | background-color: #a493af; 19 | } 20 | 21 | #webgl-canvas { 22 | outline: none; 23 | width: 100%; 24 | height: 100%; 25 | position: fixed; 26 | top: 0; 27 | left: 0; 28 | } 29 | 30 | .logo{ 31 | z-index: 0; 32 | color: rgb(218, 242, 214); 33 | position: absolute; 34 | top: 50%; 35 | left: 50%; 36 | font-size: 20vw; 37 | padding: none; 38 | transform: translate(-50%,-50%); 39 | overflow: hidden; 40 | /* stroke: #0e0e0e; 41 | -webkit-text-stroke-width: 0.001rem; 42 | -webkit-text-stroke-color: #0e0e0e; */ 43 | } 44 | .name{ 45 | font-family: 'Poppins', sans-serif; 46 | position: absolute; 47 | top: 39px; 48 | left: 50%; 49 | transform: translateX(-50%); 50 | z-index: 2; 51 | font-size: 14px; 52 | opacity: 0.7; 53 | } 54 | .cta{ 55 | font-family: 'Poppins', sans-serif; 56 | z-index: 3; 57 | font-size: 12px; 58 | text-transform: uppercase; 59 | position: absolute; 60 | top: 30px; 61 | right: 40px; 62 | text-decoration: none; 63 | color: #0e0e0e; 64 | background-color: #ffffffb5; 65 | padding: 5px 25px; 66 | border: 1px solid #0e0e0e; 67 | border-radius: 2px; 68 | transition: all 0.3s ease; 69 | border-radius: 50px; 70 | /* letter-spacing: -1px; */ 71 | } 72 | .cta:hover{ 73 | background-color: #abcaa9; 74 | /* color: rgb(228, 228, 228); */ 75 | } 76 | 77 | .socials{ 78 | position: absolute; 79 | top: 30px; 80 | left: 25px; 81 | display: flex; 82 | flex-direction: row; 83 | z-index: 3; 84 | } 85 | .socials li{ 86 | cursor: pointer; 87 | color: #0e0e0e; 88 | list-style: none; 89 | border: 1px solid #0e0e0e; 90 | background-color: #ffffffb5; 91 | padding: 4px 5px 0px 5px; 92 | margin: 0 10px; 93 | border-radius: 50%; 94 | transition: all 0.3s ease; 95 | } 96 | .socials li:hover{ 97 | background-color: #abcaa9; 98 | } 99 | .socials li img{ 100 | width: 20px; 101 | height: 20px; 102 | } 103 | .enterSceneContainer{ 104 | font-family: 'Poppins', sans-serif; 105 | z-index: 4; 106 | position: absolute; 107 | display: block; 108 | width: 100%; 109 | height: 100%; 110 | text-align: center; 111 | font-size: 25px; 112 | background-color: #a4a0a075; 113 | backdrop-filter: blur(5px); 114 | transition: all 0.5s ease; 115 | } 116 | .enterSceneContainer p{ 117 | cursor: pointer; 118 | margin: 50vh auto; 119 | background-color: #ffffff; 120 | padding: 20px 40px; 121 | width: 300px; 122 | border-radius: 50px; 123 | color: #abcaa9; 124 | transition: all 0.3s ease-in-out; 125 | } 126 | .enterSceneContainer p:hover{ 127 | background-color: #abcaa9; 128 | color: #ffffff; 129 | } 130 | .playbackControls{ 131 | z-index: 3; 132 | position: absolute; 133 | bottom: 30px; 134 | left: 50%; 135 | transform: translateX(-50%); 136 | display: flex; 137 | flex-direction: row; 138 | } 139 | .playbackControls button{ 140 | border: none; 141 | background-color: #dfdede00; 142 | cursor: pointer; 143 | margin: 20px; 144 | border-radius: 50%; 145 | padding: 5px 5px 2px 5px; 146 | display: none; 147 | } 148 | #toggleSound{ 149 | z-index: 1 150 | } 151 | button img{ 152 | width: 20px; 153 | height: 20px; 154 | } 155 | #pause,#soundOn{ 156 | display: block; 157 | } 158 | 159 | #boink{ 160 | font-family: 'Kaushan Script', cursive; 161 | position: absolute; 162 | color: #fefefe; 163 | padding: 2px 10px; 164 | font-size: 20px; 165 | transform: scale(2); 166 | } 167 | .vignette{ 168 | width: 100%; 169 | height: 100vh; 170 | position: absolute; 171 | background: radial-gradient(circle, rgba(255,255,255,0) 31%, rgb(6 140 120 / 43%) 99%); 172 | z-index: 2; 173 | } 174 | @media screen and (max-width:800px) { 175 | 176 | .cta{ 177 | top: 20px; 178 | right: 20px; 179 | } 180 | .name{ 181 | position: absolute; 182 | top: auto; 183 | bottom: 20px; 184 | left: 0; 185 | bottom: 20px; 186 | width: 100%; 187 | text-align: center; 188 | transform: translateX(0); 189 | font-size: 10px; 190 | opacity: 0.7; 191 | } 192 | .socials{ 193 | top: 20px; 194 | left: 20px; 195 | } 196 | .socials li{ 197 | padding: 4px 2px -1px 2px; 198 | margin: 0; 199 | margin-right: 10px; 200 | border-radius: 50%; 201 | transition: all 0.3s ease; 202 | } 203 | } -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /thebenezerHex.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebenezer/TheatreTutorial_Part1/d56333e7adcecfa386a23511484e8512247c61fc/thebenezerHex.ico -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "noEmit": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "skipLibCheck": true 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@theatre/core@*", "@theatre/core@^0.6.0": 6 | "integrity" "sha512-HyOoEQapeVbgKBBVfv1rjklN8oAY1/6RZuzntdL9Nfqb5UVzyVR0Im9q4WBPHx6d1dB1BKpAwIw6PzqlKoyehA==" 7 | "resolved" "https://registry.npmjs.org/@theatre/core/-/core-0.6.0.tgz" 8 | "version" "0.6.0" 9 | dependencies: 10 | "@theatre/dataverse" "0.6.0" 11 | 12 | "@theatre/dataverse@0.6.0": 13 | "integrity" "sha512-1W7b8bQTZeRmmrY64HTQzEtjLJWBmdOkfK/liR+LSSjgAylY3CjZ4/vaDUMmRQr4zCeQjyNVmrvwaIZ/33s+tQ==" 14 | "resolved" "https://registry.npmjs.org/@theatre/dataverse/-/dataverse-0.6.0.tgz" 15 | "version" "0.6.0" 16 | dependencies: 17 | "lodash-es" "^4.17.21" 18 | 19 | "@theatre/studio@^0.6.0": 20 | "integrity" "sha512-DH53wWSkIDevbYJUybLH5nIx+rFgiWlykYOmDKC9WK9QUQFYRpnaBu4UNIo+RmGZ+I2ZMSHx1RfrfnBJCr1j6A==" 21 | "resolved" "https://registry.npmjs.org/@theatre/studio/-/studio-0.6.0.tgz" 22 | "version" "0.6.0" 23 | dependencies: 24 | "@theatre/dataverse" "0.6.0" 25 | 26 | "@types/three@^0.141.0": 27 | "integrity" "sha512-OJdKDgTPVBUgc+s74DYoy4aLznbFFC38Xm4ElmU1YwGNgR7GGFVvFCX7lpVgOsT6S1zSJtGdajTsOYE8/xY9nA==" 28 | "resolved" "https://registry.npmjs.org/@types/three/-/three-0.141.0.tgz" 29 | "version" "0.141.0" 30 | dependencies: 31 | "@types/webxr" "*" 32 | 33 | "@types/webxr@*": 34 | "integrity" "sha512-LQvrACV3Pj17GpkwHwXuTd733gfY+D7b9mKdrTmLdO7vo7P/o6209Qqtk63y/FCv/lspdmi0pWz6Qe/ull9kQg==" 35 | "resolved" "https://registry.npmjs.org/@types/webxr/-/webxr-0.4.0.tgz" 36 | "version" "0.4.0" 37 | 38 | "esbuild-darwin-arm64@0.14.48": 39 | "integrity" "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==" 40 | "resolved" "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz" 41 | "version" "0.14.48" 42 | 43 | "esbuild@^0.14.27": 44 | "integrity" "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==" 45 | "resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz" 46 | "version" "0.14.48" 47 | optionalDependencies: 48 | "esbuild-android-64" "0.14.48" 49 | "esbuild-android-arm64" "0.14.48" 50 | "esbuild-darwin-64" "0.14.48" 51 | "esbuild-darwin-arm64" "0.14.48" 52 | "esbuild-freebsd-64" "0.14.48" 53 | "esbuild-freebsd-arm64" "0.14.48" 54 | "esbuild-linux-32" "0.14.48" 55 | "esbuild-linux-64" "0.14.48" 56 | "esbuild-linux-arm" "0.14.48" 57 | "esbuild-linux-arm64" "0.14.48" 58 | "esbuild-linux-mips64le" "0.14.48" 59 | "esbuild-linux-ppc64le" "0.14.48" 60 | "esbuild-linux-riscv64" "0.14.48" 61 | "esbuild-linux-s390x" "0.14.48" 62 | "esbuild-netbsd-64" "0.14.48" 63 | "esbuild-openbsd-64" "0.14.48" 64 | "esbuild-sunos-64" "0.14.48" 65 | "esbuild-windows-32" "0.14.48" 66 | "esbuild-windows-64" "0.14.48" 67 | "esbuild-windows-arm64" "0.14.48" 68 | 69 | "fsevents@~2.3.2": 70 | "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" 71 | "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" 72 | "version" "2.3.2" 73 | 74 | "function-bind@^1.1.1": 75 | "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 76 | "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" 77 | "version" "1.1.1" 78 | 79 | "has@^1.0.3": 80 | "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" 81 | "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" 82 | "version" "1.0.3" 83 | dependencies: 84 | "function-bind" "^1.1.1" 85 | 86 | "is-core-module@^2.9.0": 87 | "integrity" "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==" 88 | "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz" 89 | "version" "2.9.0" 90 | dependencies: 91 | "has" "^1.0.3" 92 | 93 | "lodash-es@^4.17.21": 94 | "integrity" "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" 95 | "resolved" "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" 96 | "version" "4.17.21" 97 | 98 | "nanoid@^3.3.4": 99 | "integrity" "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" 100 | "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" 101 | "version" "3.3.4" 102 | 103 | "path-parse@^1.0.7": 104 | "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 105 | "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" 106 | "version" "1.0.7" 107 | 108 | "picocolors@^1.0.0": 109 | "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" 110 | "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" 111 | "version" "1.0.0" 112 | 113 | "postcss@^8.4.13": 114 | "integrity" "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==" 115 | "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz" 116 | "version" "8.4.14" 117 | dependencies: 118 | "nanoid" "^3.3.4" 119 | "picocolors" "^1.0.0" 120 | "source-map-js" "^1.0.2" 121 | 122 | "resolve@^1.22.0": 123 | "integrity" "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==" 124 | "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" 125 | "version" "1.22.1" 126 | dependencies: 127 | "is-core-module" "^2.9.0" 128 | "path-parse" "^1.0.7" 129 | "supports-preserve-symlinks-flag" "^1.0.0" 130 | 131 | "rollup@^2.59.0": 132 | "integrity" "sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ==" 133 | "resolved" "https://registry.npmjs.org/rollup/-/rollup-2.75.7.tgz" 134 | "version" "2.75.7" 135 | optionalDependencies: 136 | "fsevents" "~2.3.2" 137 | 138 | "source-map-js@^1.0.2": 139 | "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" 140 | "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" 141 | "version" "1.0.2" 142 | 143 | "supports-preserve-symlinks-flag@^1.0.0": 144 | "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" 145 | "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" 146 | "version" "1.0.0" 147 | 148 | "three@^0.142.0": 149 | "integrity" "sha512-ESjPO+3geFr+ZUfVMpMnF/eVU2uJPOh0e2ZpMFqjNca1wApS9lJb7E4MjwGIczgt9iuKd8PEm6Pfgp2bJ92Xtg==" 150 | "resolved" "https://registry.npmjs.org/three/-/three-0.142.0.tgz" 151 | "version" "0.142.0" 152 | 153 | "typescript@^4.5.4": 154 | "integrity" "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==" 155 | "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz" 156 | "version" "4.7.4" 157 | 158 | "vite@^2.9.9": 159 | "integrity" "sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw==" 160 | "resolved" "https://registry.npmjs.org/vite/-/vite-2.9.13.tgz" 161 | "version" "2.9.13" 162 | dependencies: 163 | "esbuild" "^0.14.27" 164 | "postcss" "^8.4.13" 165 | "resolve" "^1.22.0" 166 | "rollup" "^2.59.0" 167 | optionalDependencies: 168 | "fsevents" "~2.3.2" 169 | --------------------------------------------------------------------------------