-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
241 lines (221 loc) · 7.53 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>quad-shader: Render fragment shader in a quad</title>
<link rel="stylesheet" href="./style.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Hind&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet"
/>
</head>
<body>
<h1>Easy 2D shader setup with quad-shader</h1>
<p>
The
<a
href="https://github.com/nmattia/quad-shader"
target="_blank"
rel="noopener noreferrer"
><code>quad-shader</code></a
>
JavaScript library helps you build
<a
href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API"
target="_blank"
rel="noopener noreferrer"
>WebGL</a
>
animations easily. The library has zero dependencies and is extremely
lightweight (2kB gzipped). WebGL runs on the GPU, allowing for smooth,
high-frame-rate animations.
</p>
<p>
All animations on this page are generated on the fly with WebGL fragment
shaders using <code>quad-shader</code>, Vite and TypeScript.
</p>
<div class="canvas-container">
<canvas id="glcanvas-dots" />
</div>
<p>
With fragment shaders, you have control over each pixel's color, making it
perfect for Creative Coding and generating textures and patterns
procedurally. With a few lines of code, you can create intricate effects.
</p>
<p
class="codepen"
data-height="600"
data-default-tab="result"
data-slug-hash="WbbERZW"
data-pen-title="Quad Shader Demo"
data-user="nmattia"
style="
height: 300px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid;
margin: 1em 0;
padding: 1em;
"
>
<span
>See the Pen
<a href="https://codepen.io/nmattia/pen/WbbERZW"> Quad Shader Demo</a>
by Nicolas Mattia (<a href="https://codepen.io/nmattia">@nmattia</a>) on
<a href="https://codepen.io">CodePen</a>.</span
>
</p>
<h2>Getting started with <code>quad-shader</code></h2>
<p>Let's take the following animation as an example:</p>
<div class="canvas-container">
<canvas id="glcanvas-waves" />
</div>
<p>
First install <code>quad-shader</code> with
<code>npm install quad-shader</code>.
</p>
<p>
After having installed the library, all it takes is (literally) three
lines of JavaScript and a fairly short fragment shader, inlined as a
string here for simplicity:
</p>
<script type="module">
import { animate, getComputedStylePropRGBA } from "./src";
const qs = animate(
document.querySelector("#glcanvas-waves"),
`
precision lowp float;
varying vec2 vPosition;
uniform vec4 uColor;
uniform float uTime;
void main() {
float theta = atan(vPosition.y, vPosition.x);
float rho = length(vPosition.xy);
float v = mod(rho - uTime/10., .2);
float alpha = smoothstep(.1, .2, v);
alpha *= (1. - smoothstep(0., 1., rho));
gl_FragColor = alpha * uColor;
}
`,
);
qs.uniform4f("uColor", () =>
getComputedStylePropRGBA(qs.canvas, "accent-color"),
);
</script>
<p id="code-example"></p>
<p>
Assuming you have a <code>canvas</code> element on the page, you should
see the animation. Congrats!
</p>
<p>
While it is fairly concise, this snippet includes some pretty powerful
features. First, the position of the current pixel is provided as
<code>vPosition</code>. Second, the value of the <code>uTime</code>
<a
target="_blank"
rel="noopener noreferrer"
href="https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html#uniforms"
><em>uniform</em></a
>
will be continuously updated — this is done for you by
<code>quad-shader</code>'s <code>animate()</code> function.
</p>
<p>
By calling the <code>uniform4f</code> method on the object returned by
<code>animate()</code>, we could pass a static value to set the custom
<code>uColor</code> uniform. Though we can also register a callback (as is
done here) which will reflect the canvas' (inherited) CSS
<code>color</code> property at any given time — open your devtools and
have some fun modifying the properties on this page! The technique used is
described in
<a
href="https://nmattia.com/posts/2025-01-29-shader-css-properties/"
target="_blank"
rel="noopener noreferrer"
>this blog post</a
>.
</p>
<p>
All
<a
href="https://github.com/nmattia/quad-shader"
target="_blank"
rel="noopener noreferrer"
><code>uniform[1234][fi]</code></a
>
methods from WebGL are mirrored, using <code>quad-shader</code>'s simpler
API.
</p>
<div class="canvas-container">
<canvas id="glcanvas-sphere" />
</div>
<p>
Finally, and probably least obvious: the animation is only rendered when
the <code>canvas</code> element is on the screen! If you scroll past it
and the element exits the viewport, no frames will be rendered, saving on
GPU power. As soon as the element re-enters the viewport, animation will
resume, as if it had been running in the background.
</p>
<script type="module">
import { codeToHtml } from "https://esm.sh/shiki@3.0.0";
const code = document.querySelector("#code-example");
codeToHtml(
// copied from the <script> above, with modifications:
// * module imported from "quad-shader" instead of "./src"
// * querySelector uses a generic "canvas"
// * "color" is used instead of "accent-color"
`import { animate, getComputedStylePropRGBA } from "quad-shader";
const qs = animate(document.querySelector("canvas"), \`
precision lowp float;
varying vec2 vPosition;
uniform vec4 uColor;
uniform float uTime;
void main() {
float theta = atan(vPosition.y, vPosition.x);
float rho = length(vPosition.xy);
float v = mod(rho - uTime/10., .2);
float alpha *= smoothstep(.1, .2, v);
alpha *= (1. - smoothstep(0., 1., rho));
gl_FragColor = alpha * uColor;
}
\`);
qs.uniform4f("uColor", () => getComputedStylePropRGBA(qs.canvas, "color"));`,
{
lang: "ts",
theme: "rose-pine",
},
).then((res) => (code.innerHTML = res));
</script>
<script
async
src="https://public.codepenassets.com/embed/index.js"
></script>
<p>
For more information check out the code for this page
<a
href="https://github.com/nmattia/quad-shader"
target="_blank"
rel="noopener noreferrer"
>on GitHub</a
>
and give it a star if you like it!
</p>
<footer>
©
<a href="https://nmattia.com" target="_blank" rel="noopener noreferrer"
>Nicolas Mattia</a
>
2025
</footer>
<script type="module">
import { main } from "./index.ts";
main();
</script>
</body>
</html>