-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.html
245 lines (240 loc) · 8.55 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
242
243
244
245
<html>
<head>
<title>Oldschool demo effects</title>
<style type="text/css">
body {
background-color: #000;
color: #eee;
font-family: sans-serif;
}
a {
color: #8ca;
font-weight: bold;
text-decoration: none;
}
h2 {
margin-left: 1ch;
border-bottom: 1px solid #aac;
}
body > div {
margin-left: 6ch;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
p {
width: 60ch;
margin-right: 2ch;
}
canvas {
border: 1px solid #fff;
vertical-align: text-top;
}
input[type=button] {
width: 10ch;
}
</style>
</head>
<body>
<h1>Oldschool Demo Effects</h1>
<div>
<div>
<p>
This is a small collection of oldschool demo effects. These effects
all use direct pixel manipulation to achieve their result. None of
them use any hardware acceleration, or anything more advanced than a
simple array of pixel data.
</p>
<p>
All effects are written in typescript. You can view the source on
<a href="https://github.com/mrkite/demofx">github</a>.
I also link to each individual effect alongside the demos below.
</p>
</div>
</div>
<h2>RotoZoom</h2>
<div>
<div>
<p>
The rotozoom is a simple effect that goes through each pixel on the
screen and maps the coordinate of that pixel backwards through a
sin/cos transform to determine which coordinate of the source image
to use. By applying a scale to that transform you also achieve the
zooming part of the rotozoom effect. Finally, the source coordinate
is wrapped using modulo to give a tiling effect.
</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/rotozoom.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('rotozoom', this)" value="Run" />
<canvas id="rotozoom" width="640" height="400" />
</div>
</div>
<h2>Fire</h2>
<div>
<div>
<p>The fire effect is a classic. The way it works is with an array
of color indices into a palette. The palette is arranged so that it
fades from black at the bottom of the palette, to orangish red in
the higher indices. On each frame, the array is traversed and each
pixel is averaged with adjacent pixels. It is then darkened slighly
so that the pixels on higher rows are darker than the ones below it.
The bottom row (just offscreen) is filled with random indices. This
becomes the fuel of the fire effect.</p>
<p>Because the fire is darkened by a constant value, the fire is
limited vertically. The classic fire effect is optimized for 55
pixels high. I do the same here, using the canvas to scale the
output.</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/fire.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('fire', this)" value="Run" />
<canvas id="fire" width="80" height="50" style="width: 640px; height:
400px" />
</div>
</div>
<h2>Plasma</h2>
<div>
<div>
<p>
There are many versions of plasma. This particular version is a
paletteless plasma which is created by adding multiple sin waves
together, combined with distortion generated doing things like
calculating the distance between pixels.
</p>
<p>
The color is determined by assigning different parts of the plasma
to different color channels. Because I use the frame time for the
inputs to the various functions, the plasma will distort and simplify
given enough time.
</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/plasma.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('plasma', this)" value="Run" />
<canvas id="plasma" width="640" height="400" />
</div>
</div>
<h2>Metablobs</h2>
<div>
<div>
<p>
To draw this effect, we loop through each pixel and calculate the
distance from that pixel to each blob. Then we multiply all these
distances together and divide by a constant. Then we subtract this
value from another constant, and clamp the result. How we choose the
various constants determines how big the blobs are and how fuzzy they
are.
</p>
<p>
Changing the number of blobs or the resolution of the screen requires
a lot of hand-adjusting the constants to give a good effect.
</p>
<p>
The blobs can move around however you like. I just have them running
in various lissajous curves.
</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/blobs.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('blob', this)" value="Run" />
<canvas id="blob" width="640" height="400" />
</div>
</div>
<h2>Tunnel</h2>
<div>
<div>
<p>
The tunnel effect is accomplished by using 3 arrays, each the size
of the screen. The first array contains the distance from that pixel
to the center of the screen, scaled and modulated by the height of
the texture. The second array contains the angle from that pixel
to the center of the screen. The third array contains a clamped
distance from the center of the screen.
</p>
<p>
These arrays are calculated only once. Then they are used to calculate
the coordinates of the texture. The first array determines the
x coordinate of the texture. The second array determines the y
coordinate. The third array determines how to darken the texture so
that the center of the tunnel is blackened.
</p>
<p>
Finally, to pan the center of the tunnel around, we actually make
the arrays twice the screen size and simply pan around the arrays.
</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/tunnel.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('tunnel', this)" value="Run" />
<canvas id="tunnel" width="640" height="400" />
</div>
</div>
<h2>Moire</h2>
<div>
<div>
<p>
The moire effect is rather simple. You have two points that act as the
foci of the moire effect. For each pixel, you calculate the
distances from that pixel to both points. XOR these distances together,
and then divide by the desired ring width. If the result is odd, make
it one color, otherwise another color. This produces the moire effect.
</p>
<p>
Again, you can move the foci points however you want, I again have them
running in lissajous curves.
</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/moire.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('moire', this)" value="Run" />
<canvas id="moire" width="640" height="400" />
</div>
</div>
<h2>Rain</h2>
<div>
<div>
<p>
The rain effect is deviously simple. There are two arrays the size
of the screen. Each element in the arrays represent the amplitiude of
the wave at that location. The algorithm takes the previous array and
averages the surrounding amplitudes and subtracts the current amplitude
from that. Then the new amplitude is dampened in order to make the
ripples die off. Then the arrays are swapped. This actually causes
the amplitude to oscillate like a wave.
We then use the amplitude to distort our texture, calculating the
delta amplitude from left to right and top to bottom. These deltas
are then used to modify the texture's coordinates and darken the
resulting pixel.
</p>
<p>
You can view the source
<a href="https://github.com/mrkite/demofx/blob/master/src/rain.ts">here</a>.
</p>
</div>
<div>
<input type="button" onclick="toggle('rain', this)" value="Run" />
<canvas id="rain" width="640" height="400" />
</div>
</div>
<script src="demo.js"></script>
</body>
</html>