├── .gitattributes
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE
├── README.md
├── deno.json
├── deno.lock
├── esbuild-script.js
├── package.json
├── src
├── constants.ts
├── deno
│ └── testUtils.ts
├── gaussianSplats.ts
├── gpuProfiler.ts
├── index.deno.ts
├── index.web.ts
├── loaders
│ └── fileSplat.ts
├── passes
│ ├── drawGroundPass.ts
│ ├── passCtx.ts
│ ├── renderSplatsGEO.ts
│ ├── renderSplatsGEO.wgsl
│ ├── renderUniformsBuffer.ts
│ ├── sortPassCPU.ts
│ ├── sortPassCPU_Naive.ts
│ └── sortPassGPU
│ │ ├── bitonicSort.test.ts
│ │ ├── bitonicSort.ts
│ │ ├── bitonicSort.wgsl
│ │ ├── calcDepths.test.ts
│ │ ├── calcDepths.ts
│ │ ├── calcDepths.wgsl
│ │ ├── index.ts
│ │ ├── unrollIndices.test.ts
│ │ ├── unrollIndices.ts
│ │ └── unrollIndices.wgsl
├── renderer.ts
├── utils.ts
└── web
│ ├── camera2.ts
│ ├── cavasResize.ts
│ ├── fpsStats.ts
│ ├── gui.ts
│ └── input.ts
├── static
├── favicon.ico
├── index.html
└── nike.splat
└── yarn.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.splat binary
2 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 |
3 | on:
4 | push:
5 | branches: [master]
6 |
7 | permissions:
8 | contents: write
9 |
10 | jobs:
11 | build-and-deploy:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - run: corepack enable
16 | - uses: actions/setup-node@v4
17 | with:
18 | node-version: '20'
19 | cache: 'yarn'
20 |
21 | - name: Build
22 | run: |
23 | yarn install
24 | yarn build
25 | touch build/.nojekyll
26 |
27 | - name: Deploy
28 | uses: JamesIves/github-pages-deploy-action@v4
29 | with:
30 | folder: build
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | *.tsbuildinfo
4 | *.code-workspace
5 | node_modules
6 |
7 | # yarn
8 | .pnp.*
9 | .yarn/*
10 | !.yarn/patches
11 | !.yarn/plugins
12 | !.yarn/releases
13 | !.yarn/sdks
14 | !.yarn/versions
15 | yarn-debug.log*
16 | yarn-error.log*
17 |
18 |
19 | # build
20 | build
21 | static/*.pem
22 |
23 |
24 | # custom
25 | *.code-workspace
26 | _references
27 | output.png
28 | makefile
29 | scenes
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2024 Marcin Matuszczyk
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 | # Gaussian Splatting WebGPU ([Math blog post](https://www.sctheblog.com/blog/gaussian-splatting/) and [DEMO](https://scthe.github.io/gaussian-splatting-webgpu/))
2 |
3 | I still remember the first time I saw [photogrammetry](https://en.wikipedia.org/wiki/Photogrammetry) in action. The artist [takes a lot of photos](https://www.youtube.com/watch?v=B5hBBFM2I_w) of a real object, plugs them into software, and receives a 3D object.
4 |
5 | In 2020, ["NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis"](https://arxiv.org/abs/2003.08934) introduced neural radiant fields. It uses a neural network to learn a 5D function (position x, y, z, viewing angle θ, ϕ) that returns a color and density/opacity. Watch the ["Why THIS is the Future of Imagery (and Nobody Knows it Yet)"](https://www.youtube.com/watch?v=YX5AoaWrowY) by Corridor Crew. Only 2 years later, Nvidia released [instant-ngp](https://github.com/NVlabs/instant-ngp) that speeds up the process. Along the way, we learned that neural networks are not even needed for this task - [Plenoxels](https://alexyu.net/plenoxels/).
6 |
7 | This leads us to ["3D Gaussian Splatting for Real-Time Radiance Field Rendering"](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/) (August 2023). The model learns a point cloud that represents a scene. Each point is a Gaussian (imagine spindle or fusiform). With enough of them, we can represent the objects with stunning accuracy.
8 |
9 | In this repo, we have a WebGPU-based online renderer for a Gaussian splat file. I have written a dedicated blog post ["Notes for 3D Gaussian Splatting renderer"](https://www.sctheblog.com/blog/gaussian-splatting/) explaining the math behind this app.
10 |
11 | > WebGPU is so new it's [not available in Firefox](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API#browser_compatibility). Use Chrome instead.
12 |
13 |
14 |
15 | 
16 |
17 | *[Nike ZoomX Vaporfly Next%](https://x.com/alexcarliera/status/1701255887107522943) model by [Alex Carlier](https://www.alexcarlier.com/).*
18 |
19 |
20 | ## Features
21 |
22 | * [Math blog post](https://www.sctheblog.com/blog/gaussian-splatting/).
23 | * Compare depth sorting strategies. Includes sorting on the GPU, and 2 algorithms on the CPU. [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) is.. terrible.
24 | * Rendering both as [Gaussians projected to a square](https://www.sctheblog.com/blog/gaussian-splatting/#method-1-project-gaussian-to-a-square) and with [eigenvectors](https://www.sctheblog.com/blog/gaussian-splatting/#method-2-calculate-eigenvectors).
25 |
26 |
27 | ## Usage
28 |
29 | WebGPU does not work on Firefox. On Chrome, it requires HTTPS even during development.
30 |
31 | 1. `openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem`. Move both files to `./static`.
32 | 2. `yarn install`.
33 | 3. `yarn dev`. Start a dev server that watches and compiles the code into `./build`. It also copies stuff from `./static`.
34 | 4. `yarn serve` in a separate terminal. Starts `http-server`. It's not included in `package.json`, but let's be honest - you already have it installed globally.
35 |
36 | Or `yarn build` for prod build.
37 |
38 | ### Camera control
39 |
40 | Use the `[W, S, A, D]` keys to move and `[Z, SPACEBAR]` to fly up or down.
41 |
42 | ### Running unit tests
43 |
44 | Node.js does not support WebGPU. Deno does (since [version 1.39](https://deno.com/blog/v1.39), December 14, 2023 - 5 months ago). Internally, it uses Firefox's [wgpu](https://github.com/gfx-rs/wgpu). It's not amazing: "WebGPU is still considered unstable in Deno". Compute is OK (with unit tests). Rendering struggles.
45 |
46 | Instruction:
47 |
48 | 1. Download the `.zip` file from [deno/releases](https://github.com/denoland/deno/releases/tag/v1.43.6).
49 | 2. `"/deno.exe" task test`
50 |
51 |
52 |
53 | ## FAQ
54 |
55 | ### What are the supported file formats?
56 |
57 | Only the .splat from [antimatter15/splat](https://github.com/antimatter15/splat). Writing file loaders is boring.
58 |
59 | ### How complicated is the renderer?
60 |
61 | 1. Load the `.ply` or `.splat` file.
62 | 2. Display it as a point cloud. Trivial using `instances=splatCount` with 4 vertices and triangle fan primitive. Then billboard it in a vertex shader.
63 | 3. Implement depth sorting (on CPU to make it easier). The Gaussians have transparency, so you need to render closest to the camera first. The simplest option is to generate an index buffer for each frame (no instancing or triangle fan). Add appropriate blend mode. At this point, your model should be recognizable.
64 | 4. Implement Gaussians. You can use my [notes](https://www.sctheblog.com/blog/gaussian-splatting/) as a reference.
65 | 5. Sorting on the GPU. The most popular (but hard to implement) is the [radix sort](https://en.wikipedia.org/wiki/Radix_sort). Instead, I've copied the [bitonic sorter](https://en.wikipedia.org/wiki/Bitonic_sorter) example code from Wikipedia. Iterate over `k` and `j` on the CPU (or as pre-generated uniform buffers) and over `i` inside the compute shader.
66 | 1. It could also be possible to kernel-fuse it, but WebGPU gets on my nerves (see below). The WGSL offers limited barriers.
67 | 2. In a non-fused approach, you need to generate 2 more shaders. One to precalculate depths by which you sort. The second one is to generate an index buffer after sorting (turn each `splatId` into 6 vertices).
68 |
69 |
70 | ### What could be some further improvements?
71 |
72 | 1. Tiled renderer, like in the original paper.
73 | 2. Spherical harmonics other than l=0;
74 | 3. Blog posts from Aras Pranckevičius:
75 | 1. ["Making Gaussian Splats smaller"](https://aras-p.info/blog/2023/09/13/Making-Gaussian-Splats-smaller/).
76 | 2. ["Making Gaussian Splats more smaller"](https://aras-p.info/blog/2023/09/27/Making-Gaussian-Splats-more-smaller/).
77 | 4. Other improvements in the field:
78 | 1. [awesome-3D-gaussian-splatting](https://github.com/MrNeRF/awesome-3D-gaussian-splatting).
79 | 2. ["Gaussian explosion"](https://aras-p.info/blog/2023/12/08/Gaussian-explosion/).
80 |
81 |
82 | ### Why does it not work on Firefox?
83 |
84 | This app uses WebGPU, a new API for writing 3D applications. Chrome has implemented the specification since [May 2023](https://developer.chrome.com/blog/webgpu-release). Firefox is still working on its [wgpu](https://github.com/gfx-rs/wgpu). Nothing I can do here.
85 |
86 | If you want to render Guassians in the browser, use antimatter15's [splat](https://github.com/antimatter15/splat) or huggingface's [gsplat.js](https://github.com/huggingface/gsplat.js). Both use WebGL, an older but more stable technology.
87 |
88 |
89 | ### Is WebGPU good?
90 |
91 | The answer will be biased, as I have [extensive](https://www.sctheblog.com/blog/vulkan-initialization/) [experience](https://www.sctheblog.com/blog/vulkan-synchronization/) [with](https://www.sctheblog.com/blog/vulkan-resources/) [Vulkan](https://www.sctheblog.com/blog/vulkan-frame/). It's like WebGPU, but 10x more complex. It would take something weird to raise my eyebrows.
92 |
93 | The WebGPU API is pleasant to use. It has compute shaders. Let's be honest, this is the killer feature. Unfortunately, this is where the positives end.
94 |
95 | * No Firefox support ATM.
96 | * The API lacks a lot of things. A few examples:
97 | * Want to check if the shader compiles correctly synchronously? Nope, [GPUCompilationInfo](https://developer.mozilla.org/en-US/docs/Web/API/GPUCompilationInfo) is not supported even in Chrome. Failed compilation operation does not even throw an error. Should I invoke the shader to check if the operation failed?! There exists an asynchronous [GPUDevice: pushErrorScope()](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/pushErrorScope) (see below). But as [GPUDevice: createShaderModule()](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/createShaderModule) is synchronous, it's a weird choice. Especially as it's common to text replace the content of the shaders before compilation. If I was given an error the moment the compilation failed, I could write a better error message.
98 | * Funny edge case: you can profile only on a per-render/compute-pass basis. Let's say you have a bitonic sort implementation that invokes shader 90 times. This leads to 180 timestamps (start + end). Oops! There was [writeTimestamp()](https://developer.mozilla.org/en-US/docs/Web/API/GPUCommandEncoder/writeTimestamp) to mark the code regions, but it was removed.
99 | * Clunky error handling. The errors are returned asynchronously, which is fine. ATM you sandwich the code between [GPUDevice: pushErrorScope()](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/pushErrorScope) and [GPUDevice: popErrorScope()](https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/popErrorScope). Here is how it works in practice:
100 | * Imagine you create a buffer on the GPU. You do some calculations on it. Read it back to the CPU. It is filled with 0. Ahh, so obvious You forgot the `GPUBufferUsage.MAP_READ` flag! But, wait. The `GPUBufferUsage.MAP_READ` is not valid with most of the other combinations of `GPUBufferUsage`. This can happen to any buffer. The error message does not even contain the label of the GPUBuffer. BTW. How granular are your error scopes?
101 | * Each unit test has to call `popErrorScope()` before **EVERY** assertion. Otherwise, if the assertion fails, the test stops. No WebGPU logs in that case. In Deno, you could try to circumvent this with ['beforeunload'](https://docs.deno.com/runtime/manual/runtime/program_lifecycle) as there is no Jest's `afterEach()`. The whole mechanism is clunky.
102 | * In Vulkan, activate [validation layers](https://github.com/KhronosGroup/Vulkan-ValidationLayers) to get tons of high-quality logs. You can even do this from the external program. No need to recompile anything. Imagine having a browser dev tool tab that can (on the developer's request) hook and synchronously display the messages.
103 | * No tooling. In JS/other languages, I rarely use debuggers. But WebGPU works on the GPU. First, you have to check if the values from the CPU were received correctly. Then you need to e.g. select which pixel to debug. The fullscreen triangle on the 1024x720px image is 737280 fragment shader calls. Sure, Deno helps with unit tests. But you know what is better? [RenderDoc](https://renderdoc.org/), like I've used [when writing Vulkan code](https://www.sctheblog.com/blog/debugging-vulkan-using-renderdoc/).
104 | * Language servers for the code editor? Forget. The ones available are missing a lot of WGLS features e.g. [overrides](https://www.w3.org/TR/WGSL/#override-decls).
105 | * [WGSL - WebGPU Shading Language](https://www.w3.org/TR/WGSL/) a Rust-like language for writing shaders. Imagine if \*Web\*GPU used syntax similar to.. I don't know.. JS?! `function` instead of `fn`, `const` and `let` instead of `let` and `var` (yes, WGSL has this reversed wrt. to JS), `:` for return type instead of `->`, etc. I love it when someone's pet project spills into formal specifications. It says a lot about the authors and the community. Ofc. it's not like **there already was GLSL for WebGL and OpenGL**. Have fun rewriting all the shaders when porting the apps!
106 |
107 | I would not say that WebGPU is dead on arrival. Compute shaders are too strong of a value proposition. Fortunately, given the massive amount of bugs (Chrome is just slightly better than Firefox), and wgpu velocity, the 'arrival' is still years away. The fact that the dev experience is hostile is an icing on the cake.
108 |
109 |
110 | ## References
111 |
112 | * ["3D Gaussian Splatting for Real-Time Radiance Field Rendering"](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/). The Gaussian splatting paper.
113 | * ["EWA Volume Splatting"](https://www.cs.umd.edu/~zwicker/publications/EWAVolumeSplatting-VIS01.pdf) or ["EWA Splatting"](https://www.cs.umd.edu/~zwicker/publications/EWASplatting-TVCG02.pdf). Math for rendering using Gaussians.
114 | * ["NumByNum :: 3D Gaussian Splatting for Real-Time Radiance Field Rendering (Kerbl et al., 2023) Reviewed"](https://medium.com/@AriaLeeNotAriel/numbynum-3d-gaussian-splatting-for-real-time-radiance-field-rendering-kerbl-et-al-60c0b25e5544) by Aria Lee.
115 | * ["Gaussian Splatting Notes"](https://github.com/kwea123/gaussian_splatting_notes) by kwea123.
116 | * [gaussian-splatting](https://github.com/graphdeco-inria/gaussian-splatting). A reference implementation from [Inria](https://www.inria.fr/en) (authors of the paper).
117 | * [splat](https://github.com/antimatter15/splat) by antimatter15. An example of WebGL implementation. Since I'm using WebGPU, I had access to compute shaders.
118 | * [UnityGaussianSplatting](https://github.com/aras-p/UnityGaussianSplatting) by Aras Pranckevičius. Mostly out of curiosity about how things can be optimized. It did help that I've recently had a lot of exposure to Unity code when writing [ai-iris-avatar](https://github.com/Scthe/ai-iris-avatar) and ["Using Unity's strand-based hair package"](https://www.sctheblog.com/blog/unity-hair/).
119 | * [Deno](https://deno.com/) for WebGPU in JS without a browser.
120 |
--------------------------------------------------------------------------------
/deno.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasks": {
3 | "start": "DENO_NO_PACKAGE_JSON=1 && deno run --allow-read=. --allow-write=. --unstable-webgpu src/index.deno.ts",
4 | "test": "DENO_NO_PACKAGE_JSON=1 && deno test --allow-read=. --allow-write=. --unstable-webgpu src"
5 | },
6 | "imports": {
7 | "png": "https://deno.land/x/pngs@0.1.1/mod.ts",
8 | "std/webgpu": "jsr:@std/webgpu@^0.224.0",
9 | "wgpu-matrix": "npm:wgpu-matrix@2.9.0",
10 | "std-path": "https://deno.land/std@0.224.0/path/mod.ts",
11 | "assert": "https://deno.land/std@0.224.0/assert/mod.ts"
12 | },
13 | "test": {
14 | "exclude": ["src/web"]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/deno.lock:
--------------------------------------------------------------------------------
1 | {
2 | "version": "3",
3 | "packages": {
4 | "specifiers": {
5 | "jsr:@std/assert@^0.225.2": "jsr:@std/assert@0.225.2",
6 | "jsr:@std/webgpu@^0.224.0": "jsr:@std/webgpu@0.224.1",
7 | "npm:wgpu-matrix@2.9.0": "npm:wgpu-matrix@2.9.0"
8 | },
9 | "jsr": {
10 | "@std/assert@0.225.2": {
11 | "integrity": "6fd566c3ea01654d29c2b633298b7fc7599716336233852eb87e9843658fa192"
12 | },
13 | "@std/webgpu@0.224.1": {
14 | "integrity": "8e4c220814f33140f7f098b049ef4e3f851ae87cf0690643cda4ce734d5d5f2e",
15 | "dependencies": [
16 | "jsr:@std/assert@^0.225.2"
17 | ]
18 | }
19 | },
20 | "npm": {
21 | "wgpu-matrix@2.9.0": {
22 | "integrity": "sha512-RFhLcfghrbYaOt7OSiNIw2bqfrl2iHA30jM0Q+fm9v5MX5gQhcRs1Eqwar3MXrv2i5N0quv8UcXTzTD1KroulQ==",
23 | "dependencies": {}
24 | }
25 | }
26 | },
27 | "remote": {
28 | "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
29 | "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
30 | "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
31 | "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
32 | "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
33 | "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
34 | "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
35 | "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
36 | "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
37 | "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
38 | "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
39 | "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
40 | "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
41 | "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
42 | "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
43 | "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
44 | "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
45 | "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
46 | "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
47 | "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
48 | "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
49 | "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
50 | "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
51 | "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
52 | "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
53 | "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
54 | "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
55 | "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
56 | "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
57 | "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
58 | "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
59 | "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
60 | "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
61 | "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8",
62 | "https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
63 | "https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c",
64 | "https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c",
65 | "https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
66 | "https://deno.land/std@0.224.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b",
67 | "https://deno.land/std@0.224.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf",
68 | "https://deno.land/std@0.224.0/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d",
69 | "https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
70 | "https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3",
71 | "https://deno.land/std@0.224.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607",
72 | "https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a",
73 | "https://deno.land/std@0.224.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883",
74 | "https://deno.land/std@0.224.0/path/_interface.ts": "8dfeb930ca4a772c458a8c7bbe1e33216fe91c253411338ad80c5b6fa93ddba0",
75 | "https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15",
76 | "https://deno.land/std@0.224.0/path/basename.ts": "7ee495c2d1ee516ffff48fb9a93267ba928b5a3486b550be73071bc14f8cc63e",
77 | "https://deno.land/std@0.224.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643",
78 | "https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36",
79 | "https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c",
80 | "https://deno.land/std@0.224.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441",
81 | "https://deno.land/std@0.224.0/path/format.ts": "6ce1779b0980296cf2bc20d66436b12792102b831fd281ab9eb08fa8a3e6f6ac",
82 | "https://deno.land/std@0.224.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069",
83 | "https://deno.land/std@0.224.0/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972",
84 | "https://deno.land/std@0.224.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7",
85 | "https://deno.land/std@0.224.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141",
86 | "https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a",
87 | "https://deno.land/std@0.224.0/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0",
88 | "https://deno.land/std@0.224.0/path/mod.ts": "f6bd79cb08be0e604201bc9de41ac9248582699d1b2ee0ab6bc9190d472cf9cd",
89 | "https://deno.land/std@0.224.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352",
90 | "https://deno.land/std@0.224.0/path/normalize_glob.ts": "cc89a77a7d3b1d01053b9dcd59462b75482b11e9068ae6c754b5cf5d794b374f",
91 | "https://deno.land/std@0.224.0/path/parse.ts": "77ad91dcb235a66c6f504df83087ce2a5471e67d79c402014f6e847389108d5a",
92 | "https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d",
93 | "https://deno.land/std@0.224.0/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0",
94 | "https://deno.land/std@0.224.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
95 | "https://deno.land/std@0.224.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1",
96 | "https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00",
97 | "https://deno.land/std@0.224.0/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2",
98 | "https://deno.land/std@0.224.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1",
99 | "https://deno.land/std@0.224.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40",
100 | "https://deno.land/std@0.224.0/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f",
101 | "https://deno.land/std@0.224.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede",
102 | "https://deno.land/std@0.224.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
103 | "https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63",
104 | "https://deno.land/std@0.224.0/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25",
105 | "https://deno.land/std@0.224.0/path/posix/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604",
106 | "https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91",
107 | "https://deno.land/std@0.224.0/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6",
108 | "https://deno.land/std@0.224.0/path/posix/parse.ts": "09dfad0cae530f93627202f28c1befa78ea6e751f92f478ca2cc3b56be2cbb6a",
109 | "https://deno.land/std@0.224.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c",
110 | "https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf",
111 | "https://deno.land/std@0.224.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf",
112 | "https://deno.land/std@0.224.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0",
113 | "https://deno.land/std@0.224.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add",
114 | "https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d",
115 | "https://deno.land/std@0.224.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b",
116 | "https://deno.land/std@0.224.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40",
117 | "https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808",
118 | "https://deno.land/std@0.224.0/path/windows/basename.ts": "6bbc57bac9df2cec43288c8c5334919418d784243a00bc10de67d392ab36d660",
119 | "https://deno.land/std@0.224.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
120 | "https://deno.land/std@0.224.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5",
121 | "https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9",
122 | "https://deno.land/std@0.224.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef",
123 | "https://deno.land/std@0.224.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6",
124 | "https://deno.land/std@0.224.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01",
125 | "https://deno.land/std@0.224.0/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8",
126 | "https://deno.land/std@0.224.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a",
127 | "https://deno.land/std@0.224.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
128 | "https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf",
129 | "https://deno.land/std@0.224.0/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25",
130 | "https://deno.land/std@0.224.0/path/windows/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604",
131 | "https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780",
132 | "https://deno.land/std@0.224.0/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6",
133 | "https://deno.land/std@0.224.0/path/windows/parse.ts": "08804327b0484d18ab4d6781742bf374976de662f8642e62a67e93346e759707",
134 | "https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7",
135 | "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972",
136 | "https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e",
137 | "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c",
138 | "https://deno.land/std@0.97.0/encoding/base64.ts": "eecae390f1f1d1cae6f6c6d732ede5276bf4b9cd29b1d281678c054dc5cc009e",
139 | "https://deno.land/x/gmath@0.1.11/mod.ts": "696f0a39bef39268a717f110b391a2280f1a25072546d88ada03bd0aba03279b",
140 | "https://deno.land/x/gmath@0.1.11/src/angle.ts": "116e5d0495900ff17352f8bab6765332d76ac672a93ec69afbb5f80576aa2169",
141 | "https://deno.land/x/gmath@0.1.11/src/decomposed.ts": "0ebd6ee5276fb74ec8f0decdd9c7b08c98609d7dc382c38696ebde3c27176ba2",
142 | "https://deno.land/x/gmath@0.1.11/src/matrix2.ts": "4b89304811d5c8cf55586a9ac103812666e4a3ccede21d12c62ca5c2a1fb3a56",
143 | "https://deno.land/x/gmath@0.1.11/src/matrix3.ts": "a198ccc90d010cde481daa24350241e63fc7eb3eab9c5d2efc88903d895b1492",
144 | "https://deno.land/x/gmath@0.1.11/src/matrix4.ts": "4a588ffcec8a2d0a922163e5425e69916533c811042dbb7f1603f136076d379e",
145 | "https://deno.land/x/gmath@0.1.11/src/projection.ts": "5f9bd9fe110276db5ad30ee4380df5d15a147ed4b75fab03728821f42ebc1a4d",
146 | "https://deno.land/x/gmath@0.1.11/src/quaternion.ts": "575d4e0ee783dfd0440593285b64a87238d022f0b758612624d65a8d2f8f915a",
147 | "https://deno.land/x/gmath@0.1.11/src/util.ts": "1b19327d98124babb35a3a19084919384f107d7dc8ea1c995538a34817f57c19",
148 | "https://deno.land/x/gmath@0.1.11/src/vector2.ts": "f9da5bd2c17aa9b4199991ab8bc64d1d7b7a2cc0425a354350c2e7c18ddfc204",
149 | "https://deno.land/x/gmath@0.1.11/src/vector3.ts": "30b920c9e98bd9fe06ee61f66016a8f7fa3235a95f3151c85c602a6b88cc0a00",
150 | "https://deno.land/x/gmath@0.1.11/src/vector4.ts": "c7240ab5e5d087dd1091ce904f721df80457894e81ce0b5a23272c5045df61ee",
151 | "https://deno.land/x/gmath@0.1.11/wasm/mod.ts": "724fed398ddc3c261e72946a73cf8cc2eee08ae599327e85e8a90d9752481ad7",
152 | "https://deno.land/x/gmath@0.1.11/wasm/wasm.js": "77db62126e548d63271853ec35371630dbe84707456f293f5ab8bd2bae6bb831",
153 | "https://deno.land/x/lz4@v0.1.2/mod.ts": "4decfc1a3569d03fd1813bd39128b71c8f082850fe98ecfdde20025772916582",
154 | "https://deno.land/x/lz4@v0.1.2/wasm.js": "b9c65605327ba273f0c76a6dc596ec534d4cda0f0225d7a94ebc606782319e46",
155 | "https://deno.land/x/pngs@0.1.1/mod.ts": "9dc8a7daed1497b94a77b68c954164a9f0b2a6f40866481bdfdbbaf015b5f764",
156 | "https://deno.land/x/pngs@0.1.1/wasm.js": "e3d4a8f293b267c9859a2164ca7b4603869bc92fe0d5ad4f109925858bce0c4c"
157 | },
158 | "workspace": {
159 | "dependencies": [
160 | "jsr:@std/webgpu@^0.224.0",
161 | "npm:wgpu-matrix@2.9.0"
162 | ],
163 | "packageJson": {
164 | "dependencies": [
165 | "npm:@types/dat.gui@^0.7.9",
166 | "npm:dat.gui@^0.7.9",
167 | "npm:esbuild-copy-static-files@^0.1.0",
168 | "npm:esbuild@^0.21.3",
169 | "npm:rimraf@^5.0.7",
170 | "npm:typescript@^5.4.5",
171 | "npm:wgpu-matrix@^2.9.0"
172 | ]
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/esbuild-script.js:
--------------------------------------------------------------------------------
1 | const esbuild = require('esbuild');
2 | const copyStaticFiles = require('esbuild-copy-static-files');
3 |
4 | const config = {
5 | entryPoints: ['./src/index.web.ts'],
6 | outdir: './build',
7 | bundle: true,
8 | define: {},
9 | loader: {
10 | '.wgsl': 'text',
11 | },
12 | // plugins
13 | plugins: [
14 | copyStaticFiles({
15 | src: './static',
16 | dest: './build',
17 | dereference: true,
18 | errorOnExist: false,
19 | recursive: true,
20 | }),
21 | ],
22 | };
23 |
24 | const defineProductionFlag = (flag) =>
25 | (config.define.IS_PRODUCTION = String(flag));
26 |
27 | async function buildProd() {
28 | console.log('Executing prod build');
29 | defineProductionFlag(true);
30 |
31 | config.minify = true;
32 | // config.format = 'esm'; // produces invalid build? some module imports or smth. 'type="module"' in
95 |