44 |
45 |
46 |
90 |
--------------------------------------------------------------------------------
/packages/logo/src/routes/logoDark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sockmaster27/svader/55b9e50a25a083118441d9d53cb8e975dec2f7b5/packages/logo/src/routes/logoDark.png
--------------------------------------------------------------------------------
/packages/logo/src/routes/shader.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 | out vec4 fragColor;
5 |
6 | uniform vec2 u_resolution;
7 | uniform vec2 u_offset;
8 | uniform float u_scale;
9 |
10 |
11 | const float pi = 3.14159265359;
12 |
13 |
14 | // Pick a pseudo-random number for the given `pos`.
15 | float random(vec2 pos) {
16 | vec2 st = pos / u_resolution;
17 | return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453);
18 | }
19 |
20 |
21 | // Create a 2-dimensional vector with a length of 1 and a pseudo-random angle.
22 | vec2 random_gradient(vec2 pos) {
23 | float angle = random(pos) * pi * 2.0;
24 | return vec2(cos(angle), sin(angle));
25 | }
26 |
27 |
28 | // Interpolate the value of `x` between 0 and 1 by using a cubic function.
29 | // When drawn on a graph, this creates an S-shaped curve between 0 and 1.
30 | float cubic_s(float x) {
31 | return x * x * (3.0 - x * 2.0);
32 | }
33 |
34 |
35 | // Create Perlin noise: https://en.wikipedia.org/wiki/Perlin_noise
36 | float perlin_noise(vec2 pos) {
37 | float cell_size = 100.0 * u_scale;
38 |
39 | vec2 grid_st = fract(pos / cell_size);
40 | vec2 grid_square_coord = floor(pos / cell_size);
41 |
42 | vec2 corner_bot_left = vec2(0.0, 0.0);
43 | vec2 corner_bot_right = vec2(1.0, 0.0);
44 | vec2 corner_top_left = vec2(0.0, 1.0);
45 | vec2 corner_top_right = vec2(1.0, 1.0);
46 |
47 | vec2 offset_bot_left = grid_st - corner_bot_left;
48 | vec2 offset_bot_right = grid_st - corner_bot_right;
49 | vec2 offset_top_left = grid_st - corner_top_left;
50 | vec2 offset_top_right = grid_st - corner_top_right;
51 |
52 | vec2 gradient_bot_left = random_gradient((grid_square_coord + corner_bot_left) * cell_size);
53 | vec2 gradient_bot_right = random_gradient((grid_square_coord + corner_bot_right) * cell_size);
54 | vec2 gradient_top_left = random_gradient((grid_square_coord + corner_top_left) * cell_size);
55 | vec2 gradient_top_right = random_gradient((grid_square_coord + corner_top_right) * cell_size);
56 |
57 | float dot_bot_left = dot(offset_bot_left, gradient_bot_left);
58 | float dot_bot_right = dot(offset_bot_right, gradient_bot_right);
59 | float dot_top_left = dot(offset_top_left, gradient_top_left);
60 | float dot_top_right = dot(offset_top_right, gradient_top_right);
61 |
62 | float x = cubic_s(grid_st.x);
63 | float y = cubic_s(grid_st.y);
64 | float bot = mix(dot_bot_left, dot_bot_right, x);
65 | float top = mix(dot_top_left, dot_top_right, x);
66 | return mix(bot, top, y);
67 | }
68 |
69 |
70 | void main() {
71 | vec2 pos = gl_FragCoord.xy + u_offset;
72 |
73 | // Mirror the y-axis to create the exact same image as the WebGPU version.
74 | pos.y = u_resolution.y - pos.y;
75 |
76 | // Multiply the noise with the vignette to create a darker spot towards the middle.
77 | float noise = perlin_noise(pos);
78 | float vignette = cubic_s(distance(pos / u_resolution, vec2(0.5, 0.5)));
79 | float r = step(0.4 - random(pos) * 0.4, vignette * noise) * 0.8;
80 |
81 | fragColor = vec4(vec3(r), 1.0);
82 | }
83 |
--------------------------------------------------------------------------------
/packages/logo/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sockmaster27/svader/55b9e50a25a083118441d9d53cb8e975dec2f7b5/packages/logo/static/favicon.png
--------------------------------------------------------------------------------
/packages/logo/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from "@sveltejs/adapter-auto";
2 |
3 | /** @type {import('@sveltejs/kit').Config} */
4 | const config = {
5 | kit: {
6 | adapter: adapter(),
7 | },
8 | };
9 |
10 | export default config;
11 |
--------------------------------------------------------------------------------
/packages/logo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "strict": true,
7 | "esModuleInterop": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "resolveJsonModule": true,
10 | "skipLibCheck": true,
11 | "sourceMap": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/logo/vite.config.js:
--------------------------------------------------------------------------------
1 | import { sveltekit } from "@sveltejs/kit/vite";
2 | import { defineConfig } from "vite";
3 |
4 | export default defineConfig({
5 | plugins: [sveltekit()],
6 | });
7 |
--------------------------------------------------------------------------------
/packages/svader/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Holger Dal Mogensen
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 |
--------------------------------------------------------------------------------
/packages/svader/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # Svader
14 |
15 | Create GPU-rendered Svelte components with WebGL and WebGPU fragment shaders.
16 |
17 | Supports Svelte 4 and Svelte 5.
18 |
19 | ## What is a fragment shader?
20 |
21 | In short, a _fragment shader_ can be written as a program that takes the coordinates of a pixel on the screen and returns the color that this pixel should have.
22 | This program can be executed on the GPU, ensuring massive parallelism and speed.
23 |
24 | To learn more about how to write fragment shaders, check out [The Book of Shaders](https://thebookofshaders.com/).
25 |
26 | The following is a collection of examples all made using Svader. The live version of all of these can be previewed on [svader.vercel.app](https://svader.vercel.app/),
27 | and the source code can be found in the [`src/routes/`](https://github.com/sockmaster27/svader/tree/master/packages/tests-svelte5/src/routes) directory.
28 |
29 | 
30 |
31 | ## Installation
32 |
33 | ```bash
34 | # npm
35 | npm i -D svader
36 |
37 | # pnpm
38 | pnpm i -D svader
39 |
40 | # Bun
41 | bun i -D svader
42 |
43 | # Yarn
44 | yarn add -D svader
45 | ```
46 |
47 | ## Usage
48 |
49 | To use a fragment shader component, you first need to decide whether to use WebGL or WebGPU.
50 | If you're unsure about what to use, see the [WebGL vs. WebGPU](#webgl-vs-webgpu) section.
51 |
52 | ### Sections
53 |
54 | - [WebGL](#webgl)
55 | - [WebGL parameters](#webgl-parameters)
56 | - [WebGL built-in values](#webgl-built-in-values)
57 | - [WebGPU](#webgpu)
58 | - [WebGPU parameters](#webgpu-parameters)
59 | - [WebGPU built-in values](#webgpu-built-in-values)
60 |
61 | ### WebGL
62 |
63 | The following is a minimal example of a WebGL fragment shader component.
64 |
65 | [**View in playground**](https://svelte.dev/playground/3e4a38bca5ca49fa94e1106a841063d5?version=5.16.2)
66 |
67 | ```svelte
68 |
87 |
88 |
97 |
WebGL not supported in this environment.
98 |
99 | ```
100 |
101 | This produces the following output:
102 |
103 | 
104 |
105 | Here, the `shaderCode` variable is a string containing the [GLES](https://en.wikipedia.org/wiki/OpenGL_ES) shader code.
106 | For simplicity, this is stored as a string, but it would typically be stored in a separate `myShader.frag` file.
107 | When loading the shader from a file, it might be useful to know that the `code` property accepts both a `string` and a `Promise`.
108 |
109 | What this code does is:
110 |
111 | 1. Add the given `u_offset` uniform to the 2D coordinates of the pixel given by `gl_FragCoord.xy`.
112 | 2. Divide the resulting coordinates entrywise by the `u_resolution` uniform to normalize the coordinates between 0 and 1.
113 | 3. Return the normalized coordinates as the color of the pixel, such that the `x` coordinate becomes the red channel and the `y` coordinate becomes the green channel. The blue channel is always set to 0, and the alpha (opacity) channel is always set to 1 (fully opaque).
114 |
115 | In GLES, _uniforms_ are inputs to the shader program, that are the same for every pixel on the screen.
116 | These need to be passed in via the `parameters` property of the `` component.
117 | In this case, we need to pass in two uniforms: `u_resolution` and `u_offset`.
118 | Since these specific parameters are very commonly used, they are specially implemented in Svader
119 | such that the `value` property of each parameter can simply be set to `"resolution"` and `"offset"` respectively.
120 |
121 | Lastly, the `` component accepts a fallback slot, which is rendered when the browser cannot render the shader.
122 |
123 | #### WebGL parameters
124 |
125 | The `parameters` property is an array of objects with the following properties:
126 |
127 | - **`name`**: The name of the uniform parameter, e.g. `"my_uniform"`.
128 | This must match the name of the parameter in the shader code.
129 |
130 | - **`type`**: The type of the uniform parameter as it is written in the shader code, e.g. `"float"`.
131 | If the `value` property is a [built-in value](#webgl-built-in-values), such as `"resolution"`,
132 | the `type` will be determined automatically and should not be set.
133 |
134 | - **`value`**: The value of the uniform parameter, or a string specifying a [built-in value](#webgl-built-in-values).
135 | If not a built-in value, the type of this property must correspond to the `type` property, such that:
136 | - **`float`, `int`, `uint`** is a `number`,
137 | - **`vecN`, `ivecN`, `uvecN`** is a `number[]` with a length of `N`, e.g. `vec2` -> `[1.2, 3.4]`.
138 | - **`matN`** is a `number[]` with a length of `N * N`, e.g. `mat2` -> `[1, 2, 3, 4]`.
139 |
140 | ##### WebGL built-in values
141 |
142 | Some types of uniforms are used very often. These are implemented in Svader itself, and referred to as _built-in values_.
143 | To use these, the `value` property of the parameter object must be set to a string matching one of the following:
144 |
145 | - **`"resolution"`**: A `vec2` of the canvas width and height in physical device pixels.
146 |
147 | - **`"scale"`**: A `float` of the ratio between CSS pixels and physical device pixels, i.e. zoom level.
148 | For example, if the browser has been zoomed to 150%, the `scale` parameter will be `1.5`.
149 |
150 | - **`"time"`**: A `float` of the current time in seconds.
151 | NOTE: Passing this parameter to the shader will cause it to rerender every frame.
152 |
153 | - **`"offset"`**: A `vec2` to be added to the `gl_FragCoord.xy` of the fragment shader.
154 | Sometimes the size of the canvas is limited by hardware.
155 | To compensate for this, Svader creates a virtual canvas with a smaller cutout shifting around to cover the screen.
156 | The `"resolution"` parameter is automatically adjusted to match the size of this virtual canvas, but for technical reasons,
157 | the `gl_FragCoord.xy` cannot be adjusted from the outside.
158 | Therefore, the `"offset"` parameter is provided to be manually added to these coordinates.
159 |
160 | ### WebGPU
161 |
162 | The following is a minimal example of a WebGPU fragment shader component.
163 |
164 | [**View in playground**](https://svelte.dev/playground/498446d091964bb199e6a88bce90feae?version=5.16.3)
165 |
166 | ```svelte
167 |
182 |
183 |
192 |
WebGPU not supported in this environment.
193 |
194 | ```
195 |
196 | This produces the following output:
197 |
198 | 
199 |
200 | Here, the `shaderCode` variable is a string containing the [WGSL](https://google.github.io/tour-of-wgsl/) shader code.
201 | For simplicity, this is stored as a string, but it would typically be stored in a separate `myShader.wgsl` file.
202 | When loading the shader from a file, it might be useful to know that the `code` property accepts both a `string` and a `Promise`.
203 |
204 | What this code does is:
205 |
206 | 1. Add the given `offset` uniform variable to the 2D coordinates of the pixel given by `raw_pos.xy`.
207 | 2. Divide the resulting coordinates entrywise by the `resolution` uniform to normalize the coordinates between 0 and 1.
208 | 3. Return the normalized coordinates as the color of the pixel, such that the `x` coordinate becomes the red channel and the `y` coordinate becomes the green channel. The blue channel is always set to 0, and the alpha (opacity) channel is always set to 1 (fully opaque).
209 |
210 | In WGSL, these `var`s are the primary way to pass in parameters to the shader.
211 | These need to be passed in via the `parameters` property of the `` component.
212 | In this case, we need to pass in two uniforms: `resolution` and `offset`.
213 | Since these specific parameters are very commonly used, they are specially implemented in Svader
214 | such that the `value` property of each parameter can simply be set to `"resolution"` and `"offset"` respectively.
215 |
216 | Lastly, the `` component accepts a fallback slot, which is rendered when the browser cannot render the shader.
217 |
218 | #### WebGPU parameters
219 |
220 | The `parameters` property is an array of objects with the following properties:
221 |
222 | - **`label`**: The name of the parameter to be used for debugging.
223 | This does not have to correspond to the name of the parameter in the shader code.
224 |
225 | - **`binding`**: An integer used to match the parameter to the variable in the shader code.
226 | This has to match the `binding` property of the parameter in the shader code, e.g. for the variable declaration
227 |
228 | ```WGSL
229 | @group(0) @binding(42) var my_variable: f32;
230 | ```
231 |
232 | the `binding` property should be `42`.
233 |
234 | - **`value`**: The value of the parameter, or a string specifying a [built-in value](#webgpu-built-in-values).
235 | If not a built-in value, this parameter should be an `ArrayBuffer`/`ArrayBufferView`.
236 | For example, to pass in a number to an `f32` parameter, it can be constructed like `new Float32Array([myNumberValue])`.
237 |
238 | - **`storage`**: [Optional - defaults to `false`] Whether the parameter is a storage variable rather than a uniform variable.
239 | This has to match the declaration in the shader code, e.g. for the variable declaration
240 |
241 | ```WGSL
242 | @group(0) @binding(0) var my_variable: f32;
243 | ```
244 |
245 | the `storage` property should be `false` or omitted, and for
246 |
247 | ```WGSL
248 | @group(0) @binding(0) var my_variable: f32;
249 | ```
250 |
251 | it should be `true`.
252 | Note that Svader currently only supports `var` and not `var`.
253 |
254 | ##### WebGPU built-in values
255 |
256 | Some types of inputs are used very often. These are implemented in Svader itself, and referred to as _built-in values_.
257 | To use these, the `value` property of the parameter object must be set to a string matching one of the following:
258 |
259 | - **`"resolution"`**: A `vec2f` of the canvas width and height in physical device pixels.
260 |
261 | - **`"scale"`**: An `f32` of the ratio between CSS pixels and physical device pixels, i.e. zoom level.
262 | For example, if the browser has been zoomed to 150%, the `scale` parameter will be `1.5`.
263 |
264 | - **`"time"`**: An `f32` of the current time in seconds.
265 | NOTE: Passing this parameter to the shader will cause it to rerender every frame.
266 |
267 | - **`"offset"`**: A `vec2f` to be added to the `@builtin(position)` of the fragment shader.
268 | Sometimes the size of the canvas is limited by hardware.
269 | To compensate for this, Svader creates a virtual canvas with a smaller cutout shifting around to cover the screen.
270 | The `"resolution"` parameter is automatically adjusted to match the size of this virtual canvas, but for technical reasons,
271 | the `@builtin(position)` cannot be adjusted from the outside.
272 | Therefore, the `"offset"` parameter is provided to be manually added to these coordinates.
273 |
274 | ## WebGL vs. WebGPU
275 |
276 | **For practical applications, default to using WebGL.**
277 |
278 | WebGL and WebGPU are both rendering APIs that allow web applications to render GPU-accelerated graphics.
279 |
280 | WebGL is the older of the two and is supported by [all modern browsers](https://caniuse.com/webgl).
281 |
282 | WebGPU is still in the experimental stage and is only supported in a [few browsers](https://caniuse.com/webgpu).
283 | However, it supports certain features that WebGL does not. For example, as of writing, WebGL in Google Chrome only supports having 8 canvases active in the document at once, while WebGPU supports a practically unlimited number.
284 |
285 | ## License
286 |
287 | Svader is licensed under the [MIT License](https://github.com/sockmaster27/svader/blob/master/LICENSE.md).
288 |
--------------------------------------------------------------------------------
/packages/svader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svader",
3 | "version": "0.5.5",
4 | "description": "Create GPU-rendered Svelte components",
5 | "author": "Holger Dal Mogensen",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/sockmaster27/svader.git"
10 | },
11 | "keywords": [
12 | "svader",
13 | "svelte",
14 | "svelte4",
15 | "svelte5",
16 | "svelte-components",
17 | "webgpu",
18 | "webgl",
19 | "gpu",
20 | "accelerated",
21 | "graphics",
22 | "fragment",
23 | "shader",
24 | "shaders"
25 | ],
26 | "scripts": {
27 | "package": "svelte-kit sync && svelte-package && publint",
28 | "prepublishOnly": "npm run package",
29 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
30 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
31 | },
32 | "exports": {
33 | ".": {
34 | "types": "./dist/index.d.ts",
35 | "svelte": "./dist/index.js"
36 | }
37 | },
38 | "files": [
39 | "dist",
40 | "!dist/**/*.test.*",
41 | "!dist/**/*.spec.*"
42 | ],
43 | "peerDependencies": {
44 | "svelte": "^5.0.0||^4.0.0"
45 | },
46 | "devDependencies": {
47 | "@webgpu/types": "^0.1.66",
48 | "svelte": "^5.0.0"
49 | },
50 | "svelte": "./dist/index.js",
51 | "types": "./dist/index.d.ts",
52 | "type": "module"
53 | }
54 |
--------------------------------------------------------------------------------
/packages/svader/src/lib/BaseShader.svelte:
--------------------------------------------------------------------------------
1 |
185 |
186 | {#if canRender}
187 |
58 |
59 |
60 |
99 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-bubbles/webgl/shader.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 | out vec4 fragColor;
5 |
6 | uniform vec2 u_resolution;
7 | uniform vec2 u_offset;
8 | uniform float u_scale;
9 | uniform float u_time;
10 | uniform vec3 u_color;
11 |
12 |
13 | // Determine the value of the pixel at `pos`, if we want to draw a circle at `center` with a the given `radius`.
14 | float circle(vec2 pos, vec2 center, float radius) {
15 | return 1.0 - smoothstep(radius - 1.5, radius, distance(pos, center));
16 | }
17 |
18 |
19 | // Determine the value of the pixel at `pos`, if we want to draw a 'bubble' (wobbly circle) at `center` with a the given `radius`.
20 | // The `random_seed` is used to make all bubbles wobble a bit differently.
21 | float bubble(vec2 pos, vec2 center, float radius, float random_seed) {
22 | const float pi = 3.1415926535897932384626433832795;
23 |
24 | // Determine the angle between `pos` and `center`.
25 | float angle = atan(pos.y - center.y, pos.x - center.x) / pi;
26 |
27 | // Create wobble by layering multiple sine waves.
28 | // One with a frequency of 1, one with a frequency of 2, etc. up to 6.
29 | float wobble = 0.0;
30 | for (float i = 1.0; i <= 6.0; i++) {
31 | float random_offset = sin(random_seed) * pi * pow(i, 2.0);
32 | float speed = pow(i, 2.0) * 0.05;
33 | float amplitude = 0.05 / i;
34 | wobble += sin(angle * i * pi + u_time * speed + random_offset) * amplitude;
35 | }
36 |
37 | float wobbly_radius = radius * (wobble + 1.0);
38 | return circle(pos, center, wobbly_radius);
39 | }
40 |
41 |
42 | void main() {
43 | vec2 pos = gl_FragCoord.xy + u_offset;
44 |
45 | // Mirror the y-axis so that the origin is in the top-left corner.
46 | pos.y = u_resolution.y - pos.y;
47 |
48 | // Break the screen into square tiles with a width of 300 CSS pixels each.
49 | float tile_width = 300.0 * u_scale;
50 | vec2 tile_center = vec2(tile_width / 2.0);
51 | vec2 tile_coords = mod(pos + tile_center, tile_width);
52 |
53 | // Determine which tile we're in and use that as a random seed.
54 | vec2 which_tile = floor((pos + tile_center) / tile_width);
55 | float tile_index = which_tile.x + which_tile.y * 10.0; // Assume at most 10 tiles in width
56 | float random_seed = tile_index;
57 |
58 | float bubble_gap = 20.0 * u_scale;
59 | float radius = tile_width / 2.0 - bubble_gap;
60 |
61 | float v = bubble(tile_coords, tile_center, radius, random_seed);
62 |
63 | fragColor = vec4(u_color * v, v);
64 | }
65 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-bubbles/webgpu/+page.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
54 |
55 |
56 |
SVADER
57 |
58 |
59 |
98 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-bubbles/webgpu/shader.wgsl:
--------------------------------------------------------------------------------
1 | @group(0) @binding(0) var offset: vec2f;
2 | @group(0) @binding(1) var scale: f32;
3 | @group(0) @binding(2) var time: f32;
4 | @group(0) @binding(3) var color: vec3f;
5 |
6 |
7 | // Determine the value of the pixel at `pos`, if we want to draw a circle at `center` with a the given `radius`.
8 | fn circle(pos: vec2f, center: vec2f, radius: f32) -> f32 {
9 | return 1.0 - smoothstep(radius - 1.5, radius, distance(pos, center));
10 | }
11 |
12 |
13 | // Determine the value of the pixel at `pos`, if we want to draw a 'bubble' (wobbly circle) at `center` with a the given `radius`.
14 | // The `random_seed` is used to make all bubbles wobble a bit differently.
15 | fn bubble(pos: vec2f, center: vec2f, radius: f32, random_seed: f32) -> f32 {
16 | let pi = 3.1415926535897932384626433832795;
17 |
18 | // Determine the angle between `pos` and `center`.
19 | let angle = atan2(pos.y - center.y, pos.x - center.x) / pi;
20 |
21 | // Create wobble by layering multiple sine waves.
22 | // One with a frequency of 1, one with a frequency of 2, etc. up to 6.
23 | var wobble = 0.0;
24 | for (var i = 1.0; i <= 6.0; i += 1.0) {
25 | let random_offset = sin(random_seed) * pi * pow(i, 2.0);
26 | let speed = pow(i, 2.0) * 0.05;
27 | let amplitude = 0.05 / i;
28 | wobble += sin(angle * i * pi + time * speed + random_offset) * amplitude;
29 | }
30 |
31 | let wobbly_radius = radius * (wobble + 1.0);
32 | return circle(pos, center, wobbly_radius);
33 | }
34 |
35 |
36 | @fragment
37 | fn main(@builtin(position) raw_pos: vec4f) -> @location(0) vec4f {
38 | let pos = raw_pos.xy + offset;
39 |
40 | // Break the screen into square tiles with a width of 300 CSS pixels each.
41 | let tile_width = 300.0 * scale;
42 | let tile_center = vec2f(tile_width / 2.0);
43 | let tile_coords = (pos + tile_center) % tile_width;
44 |
45 | // Determine which tile we're in and use that as a random seed.
46 | let which_tile = floor((pos + tile_center) / tile_width);
47 | let tile_index = which_tile.x + which_tile.y * 10.0; // Assume at most 10 tiles in width
48 | let random_seed = tile_index;
49 |
50 | let bubble_gap = 20.0 * scale;
51 | let radius = tile_width / 2.0 - bubble_gap;
52 |
53 | let v = bubble(tile_coords, tile_center, radius, random_seed);
54 |
55 | return vec4f(color * v, v);
56 | }
57 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-halo/+page.svelte:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 |
74 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-halo/webgl/shader.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 | out vec4 fragColor;
5 |
6 | uniform vec2 u_resolution;
7 | uniform vec2 u_offset;
8 |
9 |
10 | // Pick a pseudo-random number for the given `pos`.
11 | float random(vec2 pos) {
12 | vec2 st = pos / u_resolution;
13 | return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123);
14 | }
15 |
16 |
17 | // Determine the value of the pixel at `pos`, if we want to draw a halo at `center` with a the given `radius`.
18 | // The `side_length` represents the smaller dimension of the canvas, and is used to scale all values accordingly.
19 | float halo(vec2 pos, vec2 center, float radius, float side_length) {
20 | float halo_solid_feather = 0.02 * side_length;
21 | float halo_thickness = 0.017 * side_length;
22 | float halo_glow_spread = 0.3 * side_length;
23 |
24 | // Create a distance field from the center.
25 | float center_d = distance(pos, center);
26 | // Create a distence field for the halo.
27 | float halo_d = abs(center_d - radius);
28 | // Draw the 'solid' part of the halo.
29 | // By multiplying by 5.0 we generate a value greater than 1.0,
30 | // which will make the color saturate into a whiter nuance and give the halo sharper edges.
31 | float halo_solid = smoothstep(halo_d, halo_d + halo_solid_feather, halo_thickness) * 5.0;
32 | // Draw the glow around the halo.
33 | float halo_glow = pow(1.0 - clamp(halo_d / halo_glow_spread, 0.0, 1.0), 5.0) * 0.4 * (0.85 + random(pos) * 0.3);
34 |
35 | return halo_solid + halo_glow;
36 | }
37 |
38 |
39 | void main() {
40 | vec2 pos = gl_FragCoord.xy + u_offset;
41 |
42 | vec2 screen_center = u_resolution / 2.0;
43 |
44 | // This shader automatically scales to fit the smallest dimension of the canvas.
45 | float side_length = min(u_resolution.x, u_resolution.y);
46 |
47 | // Draw 5 halos to create a sense of depth.
48 | float v = 0.0;
49 | for (float i = 0.0; i < 5.0; i++) {
50 | // As the halos get further back they:
51 | // - Are placed slightly higher up
52 | vec2 center = screen_center + vec2(0.0, i * 0.005 * side_length);
53 | // - Get smaller
54 | float radius = (side_length * 0.4) / (i * 0.15 + 1.0);
55 | // - Gradually fade out
56 | float fade_factor = pow(0.3, i);
57 |
58 | v += halo(pos, center, radius, side_length) * fade_factor;
59 | }
60 |
61 | // Give the halos a yellowish color.
62 | float gradient_length = 0.5 * side_length;
63 | float gradient_value = clamp((-pos.y + (u_resolution.y + gradient_length) * 0.5) / gradient_length, 0.0, 1.0);
64 | vec4 warm_color = vec4(1.0, 0.69, 0.37, 1.0);
65 | vec4 cool_color = vec4(0.67, 0.82, 1.0, 1.0);
66 | vec4 color = mix(warm_color, cool_color, gradient_value);
67 |
68 | fragColor = color * v;
69 | }
70 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-halo/webgpu/+page.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
33 |
34 |
35 |
SVADER
36 |
37 |
38 |
76 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/landing-page-halo/webgpu/shader.wgsl:
--------------------------------------------------------------------------------
1 | @group(0) @binding(0) var resolution: vec2f;
2 | @group(0) @binding(1) var offset: vec2f;
3 | @group(0) @binding(2) var scale: f32;
4 |
5 |
6 | // Pick a pseudo-random number for the given `pos`.
7 | fn random(pos: vec2f) -> f32 {
8 | let st = pos / resolution;
9 | return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123);
10 | }
11 |
12 |
13 | // Determine the value of the pixel at `pos`, if we want to draw a halo at `center` with a the given `radius`.
14 | // The `side_length` represents the smaller dimension of the canvas, and is used to scale all values accordingly.
15 | fn halo(pos: vec2f, center: vec2f, radius: f32, side_length: f32) -> f32 {
16 | let halo_solid_feather = 0.02 * side_length;
17 | let halo_thickness = 0.017 * side_length;
18 | let halo_glow_spread = 0.3 * side_length;
19 |
20 | // Create a distance field from the center.
21 | let center_d = distance(pos, center);
22 | // Create a distence field for the halo.
23 | let halo_d = abs(center_d - radius);
24 | // Draw the 'solid' part of the halo.
25 | // By multiplying by 5.0 we generate a value greater than 1.0,
26 | // which will make the color saturate into a whiter nuance and give the halo sharper edges.
27 | let halo_solid = smoothstep(halo_d, halo_d + halo_solid_feather, halo_thickness) * 5.0;
28 | // Draw the glow around the halo.
29 | let halo_glow = pow(1.0 - clamp(halo_d / halo_glow_spread, 0.0, 1.0), 5.0) * 0.4 * (0.85 + random(pos) * 0.3);
30 |
31 | return halo_solid + halo_glow;
32 | }
33 |
34 |
35 | @fragment
36 | fn main(@builtin(position) raw_pos: vec4f) -> @location(0) vec4f {
37 | let pos = raw_pos.xy + offset;
38 |
39 | let screen_center = resolution / 2.0;
40 |
41 | // This shader automatically scales to fit the smallest dimension of the canvas.
42 | let side_length = min(resolution.x, resolution.y);
43 |
44 | // Draw 5 halos to create a sense of depth.
45 | var v = 0.0;
46 | for (var i = 0.0; i < 5.0; i += 1.0) {
47 | // As the halos get further back they:
48 | // - Are placed slightly higher up
49 | let center = screen_center - vec2(0.0, i * 0.005 * side_length);
50 | // - Get smaller
51 | let radius = (side_length * 0.4) / (i * 0.15 + 1.0);
52 | // - Gradually fade out
53 | let fade_factor = pow(0.3, i);
54 |
55 | v += halo(pos, center, radius, side_length) * fade_factor;
56 | }
57 |
58 | // Give the halos a yellowish color.
59 | let gradient_length = 0.5 * side_length;
60 | let gradient_value = clamp((pos.y - (resolution.y - gradient_length) * 0.5) / gradient_length, 0.0, 1.0);
61 | let warm_color = vec4(1.0, 0.69, 0.37, 1.0);
62 | let cool_color = vec4(0.67, 0.82, 1.0, 1.0);
63 | let color = mix(warm_color, cool_color, gradient_value);
64 |
65 | return color * v;
66 | }
67 |
--------------------------------------------------------------------------------
/packages/tests-svelte4/src/routes/oversized-canvas/+page.svelte:
--------------------------------------------------------------------------------
1 |
60 |
61 |
62 |
101 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-bubbles/webgl/shader.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 | out vec4 fragColor;
5 |
6 | uniform vec2 u_resolution;
7 | uniform vec2 u_offset;
8 | uniform float u_scale;
9 | uniform float u_time;
10 | uniform vec3 u_color;
11 |
12 |
13 | // Determine the value of the pixel at `pos`, if we want to draw a circle at `center` with a the given `radius`.
14 | float circle(vec2 pos, vec2 center, float radius) {
15 | return 1.0 - smoothstep(radius - 1.5, radius, distance(pos, center));
16 | }
17 |
18 |
19 | // Determine the value of the pixel at `pos`, if we want to draw a 'bubble' (wobbly circle) at `center` with a the given `radius`.
20 | // The `random_seed` is used to make all bubbles wobble a bit differently.
21 | float bubble(vec2 pos, vec2 center, float radius, float random_seed) {
22 | const float pi = 3.1415926535897932384626433832795;
23 |
24 | // Determine the angle between `pos` and `center`.
25 | float angle = atan(pos.y - center.y, pos.x - center.x) / pi;
26 |
27 | // Create wobble by layering multiple sine waves.
28 | // One with a frequency of 1, one with a frequency of 2, etc. up to 6.
29 | float wobble = 0.0;
30 | for (float i = 1.0; i <= 6.0; i++) {
31 | float random_offset = sin(random_seed) * pi * pow(i, 2.0);
32 | float speed = pow(i, 2.0) * 0.05;
33 | float amplitude = 0.05 / i;
34 | wobble += sin(angle * i * pi + u_time * speed + random_offset) * amplitude;
35 | }
36 |
37 | float wobbly_radius = radius * (wobble + 1.0);
38 | return circle(pos, center, wobbly_radius);
39 | }
40 |
41 |
42 | void main() {
43 | vec2 pos = gl_FragCoord.xy + u_offset;
44 |
45 | // Mirror the y-axis so that the origin is in the top-left corner.
46 | pos.y = u_resolution.y - pos.y;
47 |
48 | // Break the screen into square tiles with a width of 300 CSS pixels each.
49 | float tile_width = 300.0 * u_scale;
50 | vec2 tile_center = vec2(tile_width / 2.0);
51 | vec2 tile_coords = mod(pos + tile_center, tile_width);
52 |
53 | // Determine which tile we're in and use that as a random seed.
54 | vec2 which_tile = floor((pos + tile_center) / tile_width);
55 | float tile_index = which_tile.x + which_tile.y * 10.0; // Assume at most 10 tiles in width
56 | float random_seed = tile_index;
57 |
58 | float bubble_gap = 20.0 * u_scale;
59 | float radius = tile_width / 2.0 - bubble_gap;
60 |
61 | float v = bubble(tile_coords, tile_center, radius, random_seed);
62 |
63 | fragColor = vec4(u_color * v, v);
64 | }
65 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-bubbles/webgpu/+page.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
56 |
57 |
58 |
SVADER
59 |
60 |
61 |
100 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-bubbles/webgpu/shader.wgsl:
--------------------------------------------------------------------------------
1 | @group(0) @binding(0) var offset: vec2f;
2 | @group(0) @binding(1) var scale: f32;
3 | @group(0) @binding(2) var time: f32;
4 | @group(0) @binding(3) var color: vec3f;
5 |
6 |
7 | // Determine the value of the pixel at `pos`, if we want to draw a circle at `center` with a the given `radius`.
8 | fn circle(pos: vec2f, center: vec2f, radius: f32) -> f32 {
9 | return 1.0 - smoothstep(radius - 1.5, radius, distance(pos, center));
10 | }
11 |
12 |
13 | // Determine the value of the pixel at `pos`, if we want to draw a 'bubble' (wobbly circle) at `center` with a the given `radius`.
14 | // The `random_seed` is used to make all bubbles wobble a bit differently.
15 | fn bubble(pos: vec2f, center: vec2f, radius: f32, random_seed: f32) -> f32 {
16 | let pi = 3.1415926535897932384626433832795;
17 |
18 | // Determine the angle between `pos` and `center`.
19 | let angle = atan2(pos.y - center.y, pos.x - center.x) / pi;
20 |
21 | // Create wobble by layering multiple sine waves.
22 | // One with a frequency of 1, one with a frequency of 2, etc. up to 6.
23 | var wobble = 0.0;
24 | for (var i = 1.0; i <= 6.0; i += 1.0) {
25 | let random_offset = sin(random_seed) * pi * pow(i, 2.0);
26 | let speed = pow(i, 2.0) * 0.05;
27 | let amplitude = 0.05 / i;
28 | wobble += sin(angle * i * pi + time * speed + random_offset) * amplitude;
29 | }
30 |
31 | let wobbly_radius = radius * (wobble + 1.0);
32 | return circle(pos, center, wobbly_radius);
33 | }
34 |
35 |
36 | @fragment
37 | fn main(@builtin(position) raw_pos: vec4f) -> @location(0) vec4f {
38 | let pos = raw_pos.xy + offset;
39 |
40 | // Break the screen into square tiles with a width of 300 CSS pixels each.
41 | let tile_width = 300.0 * scale;
42 | let tile_center = vec2f(tile_width / 2.0);
43 | let tile_coords = (pos + tile_center) % tile_width;
44 |
45 | // Determine which tile we're in and use that as a random seed.
46 | let which_tile = floor((pos + tile_center) / tile_width);
47 | let tile_index = which_tile.x + which_tile.y * 10.0; // Assume at most 10 tiles in width
48 | let random_seed = tile_index;
49 |
50 | let bubble_gap = 20.0 * scale;
51 | let radius = tile_width / 2.0 - bubble_gap;
52 |
53 | let v = bubble(tile_coords, tile_center, radius, random_seed);
54 |
55 | return vec4f(color * v, v);
56 | }
57 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-halo/+page.svelte:
--------------------------------------------------------------------------------
1 |
36 |
37 |
38 |
76 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-halo/webgl/shader.frag:
--------------------------------------------------------------------------------
1 | #version 300 es
2 |
3 | precision highp float;
4 | out vec4 fragColor;
5 |
6 | uniform vec2 u_resolution;
7 | uniform vec2 u_offset;
8 |
9 |
10 | // Pick a pseudo-random number for the given `pos`.
11 | float random(vec2 pos) {
12 | vec2 st = pos / u_resolution;
13 | return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123);
14 | }
15 |
16 |
17 | // Determine the value of the pixel at `pos`, if we want to draw a halo at `center` with a the given `radius`.
18 | // The `side_length` represents the smaller dimension of the canvas, and is used to scale all values accordingly.
19 | float halo(vec2 pos, vec2 center, float radius, float side_length) {
20 | float halo_solid_feather = 0.02 * side_length;
21 | float halo_thickness = 0.017 * side_length;
22 | float halo_glow_spread = 0.3 * side_length;
23 |
24 | // Create a distance field from the center.
25 | float center_d = distance(pos, center);
26 | // Create a distence field for the halo.
27 | float halo_d = abs(center_d - radius);
28 | // Draw the 'solid' part of the halo.
29 | // By multiplying by 5.0 we generate a value greater than 1.0,
30 | // which will make the color saturate into a whiter nuance and give the halo sharper edges.
31 | float halo_solid = smoothstep(halo_d, halo_d + halo_solid_feather, halo_thickness) * 5.0;
32 | // Draw the glow around the halo.
33 | float halo_glow = pow(1.0 - clamp(halo_d / halo_glow_spread, 0.0, 1.0), 5.0) * 0.4 * (0.85 + random(pos) * 0.3);
34 |
35 | return halo_solid + halo_glow;
36 | }
37 |
38 |
39 | void main() {
40 | vec2 pos = gl_FragCoord.xy + u_offset;
41 |
42 | vec2 screen_center = u_resolution / 2.0;
43 |
44 | // This shader automatically scales to fit the smallest dimension of the canvas.
45 | float side_length = min(u_resolution.x, u_resolution.y);
46 |
47 | // Draw 5 halos to create a sense of depth.
48 | float v = 0.0;
49 | for (float i = 0.0; i < 5.0; i++) {
50 | // As the halos get further back they:
51 | // - Are placed slightly higher up
52 | vec2 center = screen_center + vec2(0.0, i * 0.005 * side_length);
53 | // - Get smaller
54 | float radius = (side_length * 0.4) / (i * 0.15 + 1.0);
55 | // - Gradually fade out
56 | float fade_factor = pow(0.3, i);
57 |
58 | v += halo(pos, center, radius, side_length) * fade_factor;
59 | }
60 |
61 | // Give the halos a yellowish color.
62 | float gradient_length = 0.5 * side_length;
63 | float gradient_value = clamp((-pos.y + (u_resolution.y + gradient_length) * 0.5) / gradient_length, 0.0, 1.0);
64 | vec4 warm_color = vec4(1.0, 0.69, 0.37, 1.0);
65 | vec4 cool_color = vec4(0.67, 0.82, 1.0, 1.0);
66 | vec4 color = mix(warm_color, cool_color, gradient_value);
67 |
68 | fragColor = color * v;
69 | }
70 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-halo/webgpu/+page.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
35 |
36 |
37 |
SVADER
38 |
39 |
40 |
78 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/landing-page-halo/webgpu/shader.wgsl:
--------------------------------------------------------------------------------
1 | @group(0) @binding(0) var resolution: vec2f;
2 | @group(0) @binding(1) var offset: vec2f;
3 | @group(0) @binding(2) var scale: f32;
4 |
5 |
6 | // Pick a pseudo-random number for the given `pos`.
7 | fn random(pos: vec2f) -> f32 {
8 | let st = pos / resolution;
9 | return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123);
10 | }
11 |
12 |
13 | // Determine the value of the pixel at `pos`, if we want to draw a halo at `center` with a the given `radius`.
14 | // The `side_length` represents the smaller dimension of the canvas, and is used to scale all values accordingly.
15 | fn halo(pos: vec2f, center: vec2f, radius: f32, side_length: f32) -> f32 {
16 | let halo_solid_feather = 0.02 * side_length;
17 | let halo_thickness = 0.017 * side_length;
18 | let halo_glow_spread = 0.3 * side_length;
19 |
20 | // Create a distance field from the center.
21 | let center_d = distance(pos, center);
22 | // Create a distence field for the halo.
23 | let halo_d = abs(center_d - radius);
24 | // Draw the 'solid' part of the halo.
25 | // By multiplying by 5.0 we generate a value greater than 1.0,
26 | // which will make the color saturate into a whiter nuance and give the halo sharper edges.
27 | let halo_solid = smoothstep(halo_d, halo_d + halo_solid_feather, halo_thickness) * 5.0;
28 | // Draw the glow around the halo.
29 | let halo_glow = pow(1.0 - clamp(halo_d / halo_glow_spread, 0.0, 1.0), 5.0) * 0.4 * (0.85 + random(pos) * 0.3);
30 |
31 | return halo_solid + halo_glow;
32 | }
33 |
34 |
35 | @fragment
36 | fn main(@builtin(position) raw_pos: vec4f) -> @location(0) vec4f {
37 | let pos = raw_pos.xy + offset;
38 |
39 | let screen_center = resolution / 2.0;
40 |
41 | // This shader automatically scales to fit the smallest dimension of the canvas.
42 | let side_length = min(resolution.x, resolution.y);
43 |
44 | // Draw 5 halos to create a sense of depth.
45 | var v = 0.0;
46 | for (var i = 0.0; i < 5.0; i += 1.0) {
47 | // As the halos get further back they:
48 | // - Are placed slightly higher up
49 | let center = screen_center - vec2(0.0, i * 0.005 * side_length);
50 | // - Get smaller
51 | let radius = (side_length * 0.4) / (i * 0.15 + 1.0);
52 | // - Gradually fade out
53 | let fade_factor = pow(0.3, i);
54 |
55 | v += halo(pos, center, radius, side_length) * fade_factor;
56 | }
57 |
58 | // Give the halos a yellowish color.
59 | let gradient_length = 0.5 * side_length;
60 | let gradient_value = clamp((pos.y - (resolution.y - gradient_length) * 0.5) / gradient_length, 0.0, 1.0);
61 | let warm_color = vec4(1.0, 0.69, 0.37, 1.0);
62 | let cool_color = vec4(0.67, 0.82, 1.0, 1.0);
63 | let color = mix(warm_color, cool_color, gradient_value);
64 |
65 | return color * v;
66 | }
67 |
--------------------------------------------------------------------------------
/packages/tests-svelte5/src/routes/oversized-canvas/+page.svelte:
--------------------------------------------------------------------------------
1 |