litecanvas 0.83.4 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,16 +2,17 @@
2
2
 
3
3
  # Litecanvas
4
4
 
5
- [![NPM Version](https://flat.badgen.net/npm/v/litecanvas?scale=1&label=NPM)](https://www.npmjs.com/package/litecanvas/)
5
+ [![NPM Version](https://flat.badgen.net/npm/v/litecanvas?scale=1&label=NPM&color=blue&cache=3600)](https://www.npmjs.com/package/litecanvas/)
6
+ [![License](https://flat.badgen.net/npm/license/litecanvas)](LICENSE)
6
7
 
7
8
  Litecanvas is a lightweight HTML5 canvas 2D engine suitable for small web games, prototypes, game jams, animations, creative coding, learning game programming and game design, etc.
8
9
 
9
-
10
10
  [![Itch](https://flat.badgen.net/static/FOLLOW/ON%20ITCH.IO/fa5c5c?scale=1.25)](https://bills.itch.io/litecanvas)
11
11
  [![Discord Server](https://flat.badgen.net/static/CHAT/ON%20DISCORD/5865f2?scale=1.25&icon=discord)](https://discord.com/invite/r2c3rGsvH3)
12
12
  [![Playground](https://flat.badgen.net/static/CODE/ON%20PLAYGROUND/5f3dc4?scale=1.25)](https://litecanvas.js.org/)
13
13
 
14
- > [!WARNING]
14
+ <!-- prettier-ignore -->
15
+ > [!WARNING]
15
16
  > **This project is still in the "alpha" stage. Break changes may occur frequently. All feedback is welcome and appreciated.**
16
17
 
17
18
  ### Features
@@ -60,9 +61,9 @@ litecanvas({
60
61
  function init() {
61
62
  bg = 0 // the color #0 (black)
62
63
  color = 3 // the color #3 (white)
63
- radius = W / 10 // the canvas width/10
64
- posx = CX // center X (or canvas width/2)
65
- posy = CY // center Y (or canvas width/2)
64
+ radius = W / 10 // the canvas Width/10
65
+ posx = W / 2 // center X (or canvas Width/2)
66
+ posy = H / 2 // center Y (or canvas Height/2)
66
67
  }
67
68
 
68
69
  // this function detect clicks/touches
@@ -75,7 +76,7 @@ function tapped(x, y) {
75
76
 
76
77
  // put the game logic in this function
77
78
  function update(dt) {
78
- // make the circle falls 100 pixels per second
79
+ // make the circle falls 200 pixels per second
79
80
  posy += 200 * dt
80
81
  }
81
82
 
package/dist/dist.dev.js CHANGED
@@ -1,10 +1,13 @@
1
1
  (() => {
2
2
  // src/zzfx.js
3
- var zzfxX = /* @__PURE__ */ new AudioContext();
4
- var zzfx = (i = 1, d = 0.05, z = 220, e = 0, P = 0, S = 0.1, I = 0, c = 1, T = 0, H = 0, V = 0, J = 0, h = 0, j = 0, K = 0, E = 0, r = 0, B = 1, X = 0, L = 0, D = 0) => {
5
- let n = Math, t = 2 * n.PI, a = 44100, F = T *= 500 * t / a / a, O = z *= (1 - d + 2 * d * n.random(d = [])) * t / a, x = 0, _ = 0, f = 0, g = 1, $ = 0, l = 0, o = 0, s = D < 0 ? -1 : 1, u = t * s * D * 2 / a, G = n.cos(u), C = n.sin, Q = C(u) / 4, M = 1 + Q, m = -2 * G / M, y = (1 - Q) / M, R = (1 + s * G) / 2 / M, A = -(s + G) / M, v = R, U = 0, W = 0, Y = 0, Z = 0;
6
- for (e = a * e + 9, X *= a, P *= a, S *= a, r *= a, H *= 500 * t / a ** 3, K *= t / a, V *= t / a, J *= a, h = a * h | 0, i *= 0.3 * (globalThis.zzfxV || 1), s = e + X + P + S + r | 0; f < s; d[f++] = o * i) ++l % (100 * E | 0) || (o = I ? 1 < I ? 2 < I ? 3 < I ? C(x * x) : n.max(n.min(n.tan(x), 1), -1) : 1 - (2 * x / t % 2 + 2) % 2 : 1 - 4 * n.abs(n.round(x / t) - x / t) : C(x), o = (h ? 1 - L + L * C(t * f / h) : 1) * (o < 0 ? -1 : 1) * n.abs(o) ** c * (f < e ? f / e : f < e + X ? 1 - (f - e) / X * (1 - B) : f < e + X + P ? B : f < s - r ? (s - f - r) / S * B : 0), o = r ? o / 2 + (r > f ? 0 : (f < s - r ? 1 : (s - f) / r) * d[f - r | 0] / 2 / i) : o, D && (o = Z = v * U + A * (U = W) + R * (W = o) - y * Y - m * (Y = Z))), u = (z += T += H) * n.cos(K * _++), x += u + u * j * C(f ** 5), g && ++g > J && (z += V, O += V, g = 0), !h || ++$ % h || (z = O, T = F, g = g || 1);
7
- i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
3
+ var setupZzFX = (global) => {
4
+ const zzfxX = new AudioContext();
5
+ global.zzfxV = 1;
6
+ return (i = 1, d = 0.05, z = 220, e = 0, P = 0, S = 0.1, I = 0, c = 1, T = 0, H = 0, V = 0, J = 0, h = 0, j = 0, K = 0, E = 0, r = 0, B = 1, X = 0, L = 0, D = 0) => {
7
+ let n = Math, t = 2 * n.PI, a = 44100, F = T *= 500 * t / a / a, O = z *= (1 - d + 2 * d * n.random(d = [])) * t / a, x = 0, _ = 0, f = 0, g = 1, $ = 0, l = 0, o = 0, s = D < 0 ? -1 : 1, u = t * s * D * 2 / a, G = n.cos(u), C = n.sin, Q = C(u) / 4, M = 1 + Q, m = -2 * G / M, y = (1 - Q) / M, R = (1 + s * G) / 2 / M, A = -(s + G) / M, v = R, U = 0, W = 0, Y = 0, Z = 0;
8
+ for (e = a * e + 9, X *= a, P *= a, S *= a, r *= a, H *= 500 * t / a ** 3, K *= t / a, V *= t / a, J *= a, h = a * h | 0, i *= 0.3 * global.zzfxV, s = e + X + P + S + r | 0; f < s; d[f++] = o * i) ++l % (100 * E | 0) || (o = I ? 1 < I ? 2 < I ? 3 < I ? C(x * x) : n.max(n.min(n.tan(x), 1), -1) : 1 - (2 * x / t % 2 + 2) % 2 : 1 - 4 * n.abs(n.round(x / t) - x / t) : C(x), o = (h ? 1 - L + L * C(t * f / h) : 1) * (o < 0 ? -1 : 1) * n.abs(o) ** c * (f < e ? f / e : f < e + X ? 1 - (f - e) / X * (1 - B) : f < e + X + P ? B : f < s - r ? (s - f - r) / S * B : 0), o = r ? o / 2 + (r > f ? 0 : (f < s - r ? 1 : (s - f) / r) * d[f - r | 0] / 2 / i) : o, D && (o = Z = v * U + A * (U = W) + R * (W = o) - y * Y - m * (Y = Z))), u = (z += T += H) * n.cos(K * _++), x += u + u * j * C(f ** 5), g && ++g > J && (z += V, O += V, g = 0), !h || ++$ % h || (z = O, T = F, g = g || 1);
9
+ i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
10
+ };
8
11
  };
9
12
 
10
13
  // src/palette.js
@@ -33,7 +36,7 @@
33
36
  const root = globalThis, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
34
37
  elem.addEventListener(evt, callback, false);
35
38
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
36
- }, isNumber = Number.isFinite, defaults = {
39
+ }, zzfx = setupZzFX(root), isNumber = Number.isFinite, defaults = {
37
40
  width: null,
38
41
  height: null,
39
42
  autoscale: true,
@@ -58,8 +61,6 @@
58
61
  tapped: null
59
62
  };
60
63
  const instance = {
61
- /** @type {HTMLCanvasElement} */
62
- CANVAS: null,
63
64
  /** @type {number} */
64
65
  W: 0,
65
66
  /** @type {number} */
@@ -67,10 +68,6 @@
67
68
  /** @type {number} */
68
69
  T: 0,
69
70
  /** @type {number} */
70
- CX: 0,
71
- /** @type {number} */
72
- CY: 0,
73
- /** @type {number} */
74
71
  MX: -1,
75
72
  /** @type {number} */
76
73
  MY: -1,
@@ -156,13 +153,13 @@
156
153
  * @param {number} max
157
154
  * @returns {number}
158
155
  */
159
- clamp: (value, min2, max2) => {
156
+ clamp: (value, min, max) => {
160
157
  DEV: assert(isNumber(value), "clamp: 1st param must be a number");
161
- DEV: assert(isNumber(min2), "clamp: 2nd param must be a number");
162
- DEV: assert(isNumber(max2), "clamp: 3rd param must be a number");
163
- DEV: assert(max2 > min2, "clamp: the 2nd param must be less than the 3rd param");
164
- if (value < min2) return min2;
165
- if (value > max2) return max2;
158
+ DEV: assert(isNumber(min), "clamp: 2nd param must be a number");
159
+ DEV: assert(isNumber(max), "clamp: 3rd param must be a number");
160
+ DEV: assert(max > min, "clamp: the 2nd param must be less than the 3rd param");
161
+ if (value < min) return min;
162
+ if (value > max) return max;
166
163
  return value;
167
164
  },
168
165
  /**
@@ -173,12 +170,12 @@
173
170
  * @param {number} max
174
171
  * @returns {number}
175
172
  */
176
- wrap: (value, min2, max2) => {
173
+ wrap: (value, min, max) => {
177
174
  DEV: assert(isNumber(value), "wrap: 1st param must be a number");
178
- DEV: assert(isNumber(min2), "wrap: 2nd param must be a number");
179
- DEV: assert(isNumber(max2), "wrap: 3rd param must be a number");
180
- DEV: assert(max2 > min2, "wrap: the 2nd param must be less than the 3rd param");
181
- return value - (max2 - min2) * math.floor((value - min2) / (max2 - min2));
175
+ DEV: assert(isNumber(min), "wrap: 2nd param must be a number");
176
+ DEV: assert(isNumber(max), "wrap: 3rd param must be a number");
177
+ DEV: assert(max > min, "wrap: the 2nd param must be less than the 3rd param");
178
+ return value - (max - min) * math.floor((value - min) / (max - min));
182
179
  },
183
180
  /**
184
181
  * Re-maps a number from one range to another.
@@ -197,7 +194,7 @@
197
194
  DEV: assert(isNumber(stop1), "map: 3rd param must be a number");
198
195
  DEV: assert(isNumber(start2), "map: 4th param must be a number");
199
196
  DEV: assert(isNumber(stop2), "map: 5th param must be a number");
200
- DEV: assert(max !== min, "map: the 3rd param must be different than the 2nd param");
197
+ DEV: assert(stop1 !== start1, "map: the 2nd param must be different than the 3rd param");
201
198
  const result = (value - start1) / (stop1 - start1) * (stop2 - start2) + start2;
202
199
  return withinBounds ? instance.clamp(result, start2, stop2) : result;
203
200
  },
@@ -244,15 +241,15 @@
244
241
  * @param {number} [max=1.0]
245
242
  * @returns {number} the random number
246
243
  */
247
- rand: (min2 = 0, max2 = 1) => {
248
- DEV: assert(isNumber(min2), "rand: 1st param must be a number");
249
- DEV: assert(isNumber(max2), "rand: 2nd param must be a number");
250
- DEV: assert(max2 > min2, "rand: the 1st param must be less than the 2nd param");
244
+ rand: (min = 0, max = 1) => {
245
+ DEV: assert(isNumber(min), "rand: 1st param must be a number");
246
+ DEV: assert(isNumber(max), "rand: 2nd param must be a number");
247
+ DEV: assert(max > min, "rand: the 1st param must be less than the 2nd param");
251
248
  const a = 1664525;
252
249
  const c = 1013904223;
253
250
  const m = 4294967296;
254
251
  _rngSeed = (a * _rngSeed + c) % m;
255
- return _rngSeed / m * (max2 - min2) + min2;
252
+ return _rngSeed / m * (max - min) + min;
256
253
  },
257
254
  /**
258
255
  * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
@@ -261,11 +258,11 @@
261
258
  * @param {number} [max=1]
262
259
  * @returns {number} the random number
263
260
  */
264
- randi: (min2 = 0, max2 = 1) => {
265
- DEV: assert(isNumber(min2), "randi: 1st param must be a number");
266
- DEV: assert(isNumber(max2), "randi: 2nd param must be a number");
267
- DEV: assert(max2 > min2, "randi: the 1st param must be less than the 2nd param");
268
- return math.floor(instance.rand(min2, max2 + 1));
261
+ randi: (min = 0, max = 1) => {
262
+ DEV: assert(isNumber(min), "randi: 1st param must be a number");
263
+ DEV: assert(isNumber(max), "randi: 2nd param must be a number");
264
+ DEV: assert(max > min, "randi: the 1st param must be less than the 2nd param");
265
+ return math.floor(instance.rand(min, max + 1));
269
266
  },
270
267
  /**
271
268
  * Initializes the random number generator with an explicit seed value.
@@ -774,6 +771,14 @@
774
771
  root.zzfxV = value;
775
772
  },
776
773
  /** PLUGINS API */
774
+ /**
775
+ * Returns the canvas
776
+ *
777
+ * @returns {HTMLCanvasElement}
778
+ */
779
+ canvas() {
780
+ return _canvas;
781
+ },
777
782
  /**
778
783
  * Prepares a plugin to be loaded
779
784
  *
@@ -1165,7 +1170,7 @@
1165
1170
  function drawFrame(now) {
1166
1171
  let updated = 0;
1167
1172
  if (settings.animate) {
1168
- _accumulated += math.min(0.2, (now - _lastFrameTime) / 1e3);
1173
+ _accumulated += math.min(0.1, (now - _lastFrameTime) / 1e3);
1169
1174
  _lastFrameTime = now;
1170
1175
  while (_accumulated >= _deltaTime) {
1171
1176
  updated++;
package/dist/dist.js CHANGED
@@ -1,10 +1,13 @@
1
1
  (() => {
2
2
  // src/zzfx.js
3
- var zzfxX = /* @__PURE__ */ new AudioContext();
4
- var zzfx = (i = 1, d = 0.05, z = 220, e = 0, P = 0, S = 0.1, I = 0, c = 1, T = 0, H = 0, V = 0, J = 0, h = 0, j = 0, K = 0, E = 0, r = 0, B = 1, X = 0, L = 0, D = 0) => {
5
- let n = Math, t = 2 * n.PI, a = 44100, F = T *= 500 * t / a / a, O = z *= (1 - d + 2 * d * n.random(d = [])) * t / a, x = 0, _ = 0, f = 0, g = 1, $ = 0, l = 0, o = 0, s = D < 0 ? -1 : 1, u = t * s * D * 2 / a, G = n.cos(u), C = n.sin, Q = C(u) / 4, M = 1 + Q, m = -2 * G / M, y = (1 - Q) / M, R = (1 + s * G) / 2 / M, A = -(s + G) / M, v = R, U = 0, W = 0, Y = 0, Z = 0;
6
- for (e = a * e + 9, X *= a, P *= a, S *= a, r *= a, H *= 500 * t / a ** 3, K *= t / a, V *= t / a, J *= a, h = a * h | 0, i *= 0.3 * (globalThis.zzfxV || 1), s = e + X + P + S + r | 0; f < s; d[f++] = o * i) ++l % (100 * E | 0) || (o = I ? 1 < I ? 2 < I ? 3 < I ? C(x * x) : n.max(n.min(n.tan(x), 1), -1) : 1 - (2 * x / t % 2 + 2) % 2 : 1 - 4 * n.abs(n.round(x / t) - x / t) : C(x), o = (h ? 1 - L + L * C(t * f / h) : 1) * (o < 0 ? -1 : 1) * n.abs(o) ** c * (f < e ? f / e : f < e + X ? 1 - (f - e) / X * (1 - B) : f < e + X + P ? B : f < s - r ? (s - f - r) / S * B : 0), o = r ? o / 2 + (r > f ? 0 : (f < s - r ? 1 : (s - f) / r) * d[f - r | 0] / 2 / i) : o, D && (o = Z = v * U + A * (U = W) + R * (W = o) - y * Y - m * (Y = Z))), u = (z += T += H) * n.cos(K * _++), x += u + u * j * C(f ** 5), g && ++g > J && (z += V, O += V, g = 0), !h || ++$ % h || (z = O, T = F, g = g || 1);
7
- i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
3
+ var setupZzFX = (global) => {
4
+ const zzfxX = new AudioContext();
5
+ global.zzfxV = 1;
6
+ return (i = 1, d = 0.05, z = 220, e = 0, P = 0, S = 0.1, I = 0, c = 1, T = 0, H = 0, V = 0, J = 0, h = 0, j = 0, K = 0, E = 0, r = 0, B = 1, X = 0, L = 0, D = 0) => {
7
+ let n = Math, t = 2 * n.PI, a = 44100, F = T *= 500 * t / a / a, O = z *= (1 - d + 2 * d * n.random(d = [])) * t / a, x = 0, _ = 0, f = 0, g = 1, $ = 0, l = 0, o = 0, s = D < 0 ? -1 : 1, u = t * s * D * 2 / a, G = n.cos(u), C = n.sin, Q = C(u) / 4, M = 1 + Q, m = -2 * G / M, y = (1 - Q) / M, R = (1 + s * G) / 2 / M, A = -(s + G) / M, v = R, U = 0, W = 0, Y = 0, Z = 0;
8
+ for (e = a * e + 9, X *= a, P *= a, S *= a, r *= a, H *= 500 * t / a ** 3, K *= t / a, V *= t / a, J *= a, h = a * h | 0, i *= 0.3 * global.zzfxV, s = e + X + P + S + r | 0; f < s; d[f++] = o * i) ++l % (100 * E | 0) || (o = I ? 1 < I ? 2 < I ? 3 < I ? C(x * x) : n.max(n.min(n.tan(x), 1), -1) : 1 - (2 * x / t % 2 + 2) % 2 : 1 - 4 * n.abs(n.round(x / t) - x / t) : C(x), o = (h ? 1 - L + L * C(t * f / h) : 1) * (o < 0 ? -1 : 1) * n.abs(o) ** c * (f < e ? f / e : f < e + X ? 1 - (f - e) / X * (1 - B) : f < e + X + P ? B : f < s - r ? (s - f - r) / S * B : 0), o = r ? o / 2 + (r > f ? 0 : (f < s - r ? 1 : (s - f) / r) * d[f - r | 0] / 2 / i) : o, D && (o = Z = v * U + A * (U = W) + R * (W = o) - y * Y - m * (Y = Z))), u = (z += T += H) * n.cos(K * _++), x += u + u * j * C(f ** 5), g && ++g > J && (z += V, O += V, g = 0), !h || ++$ % h || (z = O, T = F, g = g || 1);
9
+ i = zzfxX.createBuffer(1, s, a), i.getChannelData(0).set(d), z = zzfxX.createBufferSource(), z.buffer = i, z.connect(zzfxX.destination), z.start();
10
+ };
8
11
  };
9
12
 
10
13
  // src/palette.js
@@ -28,7 +31,7 @@
28
31
  const root = globalThis, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
29
32
  elem.addEventListener(evt, callback, false);
30
33
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
31
- }, isNumber = Number.isFinite, defaults = {
34
+ }, zzfx = setupZzFX(root), isNumber = Number.isFinite, defaults = {
32
35
  width: null,
33
36
  height: null,
34
37
  autoscale: true,
@@ -53,8 +56,6 @@
53
56
  tapped: null
54
57
  };
55
58
  const instance = {
56
- /** @type {HTMLCanvasElement} */
57
- CANVAS: null,
58
59
  /** @type {number} */
59
60
  W: 0,
60
61
  /** @type {number} */
@@ -62,10 +63,6 @@
62
63
  /** @type {number} */
63
64
  T: 0,
64
65
  /** @type {number} */
65
- CX: 0,
66
- /** @type {number} */
67
- CY: 0,
68
- /** @type {number} */
69
66
  MX: -1,
70
67
  /** @type {number} */
71
68
  MY: -1,
@@ -141,9 +138,9 @@
141
138
  * @param {number} max
142
139
  * @returns {number}
143
140
  */
144
- clamp: (value, min2, max2) => {
145
- if (value < min2) return min2;
146
- if (value > max2) return max2;
141
+ clamp: (value, min, max) => {
142
+ if (value < min) return min;
143
+ if (value > max) return max;
147
144
  return value;
148
145
  },
149
146
  /**
@@ -154,8 +151,8 @@
154
151
  * @param {number} max
155
152
  * @returns {number}
156
153
  */
157
- wrap: (value, min2, max2) => {
158
- return value - (max2 - min2) * math.floor((value - min2) / (max2 - min2));
154
+ wrap: (value, min, max) => {
155
+ return value - (max - min) * math.floor((value - min) / (max - min));
159
156
  },
160
157
  /**
161
158
  * Re-maps a number from one range to another.
@@ -205,12 +202,12 @@
205
202
  * @param {number} [max=1.0]
206
203
  * @returns {number} the random number
207
204
  */
208
- rand: (min2 = 0, max2 = 1) => {
205
+ rand: (min = 0, max = 1) => {
209
206
  const a = 1664525;
210
207
  const c = 1013904223;
211
208
  const m = 4294967296;
212
209
  _rngSeed = (a * _rngSeed + c) % m;
213
- return _rngSeed / m * (max2 - min2) + min2;
210
+ return _rngSeed / m * (max - min) + min;
214
211
  },
215
212
  /**
216
213
  * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
@@ -219,8 +216,8 @@
219
216
  * @param {number} [max=1]
220
217
  * @returns {number} the random number
221
218
  */
222
- randi: (min2 = 0, max2 = 1) => {
223
- return math.floor(instance.rand(min2, max2 + 1));
219
+ randi: (min = 0, max = 1) => {
220
+ return math.floor(instance.rand(min, max + 1));
224
221
  },
225
222
  /**
226
223
  * Initializes the random number generator with an explicit seed value.
@@ -586,6 +583,14 @@
586
583
  root.zzfxV = value;
587
584
  },
588
585
  /** PLUGINS API */
586
+ /**
587
+ * Returns the canvas
588
+ *
589
+ * @returns {HTMLCanvasElement}
590
+ */
591
+ canvas() {
592
+ return _canvas;
593
+ },
589
594
  /**
590
595
  * Prepares a plugin to be loaded
591
596
  *
@@ -947,7 +952,7 @@
947
952
  function drawFrame(now) {
948
953
  let updated = 0;
949
954
  if (settings.animate) {
950
- _accumulated += math.min(0.2, (now - _lastFrameTime) / 1e3);
955
+ _accumulated += math.min(0.1, (now - _lastFrameTime) / 1e3);
951
956
  _lastFrameTime = now;
952
957
  while (_accumulated >= _deltaTime) {
953
958
  updated++;
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,o=.1,r=0,s=1,f=0,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,b=1,v=0,x=0,y=0)=>{let C=Math,k=2*C.PI,E=f*=500*k/44100/44100,T=l*=(1-a+2*a*C.random(a=[]))*k/44100,z=0,A=0,S=0,I=1,P=0,X=0,Y=0,L=y<0?-1:1,M=k*L*y*2/44100,N=C.cos(M),H=C.sin,W=H(M)/4,D=1+W,F=-2*N/D,V=(1-W)/D,q=(1+L*N)/2/D,B=-(L+N)/D,O=0,R=0,G=0,$=0;for(n=44100*n+9,v*=44100,i*=44100,o*=44100,w*=44100,c*=500*k/85766121e6,g*=k/44100,d*=k/44100,p*=44100,u=44100*u|0,t*=.3*(globalThis.zzfxV||1),L=n+v+i+o+w|0;S<L;a[S++]=Y*t)++X%(100*m|0)||(Y=r?1<r?2<r?3<r?H(z*z):C.max(C.min(C.tan(z),1),-1):1-(2*z/k%2+2)%2:1-4*C.abs(C.round(z/k)-z/k):H(z),Y=(u?1-x+x*H(k*S/u):1)*(Y<0?-1:1)*C.abs(Y)**s*(S<n?S/n:S<n+v?1-(S-n)/v*(1-b):S<n+v+i?b:S<L-w?(L-S-w)/o*b:0),Y=w?Y/2+(w>S?0:(S<L-w?1:(L-S)/w)*a[S-w|0]/2/t):Y,y&&(Y=$=q*O+B*(O=R)+q*(R=Y)-V*G-F*(G=$))),z+=(M=(l+=f+=c)*C.cos(g*A++))+M*h*H(S**5),I&&++I>p&&(l+=d,T+=d,I=0),!u||++P%u||(l=T,f=E,I=I||1);(t=e.createBuffer(1,L,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math,i=2*n.PI,o=requestAnimationFrame,r=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,b=0,v,x="sans-serif",y=20,C=Date.now(),k=a,E=[.5,0,1750,,,.3,1,,,,600,.1],T={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},z={CANVAS:null,W:0,H:0,T:0,CX:0,CY:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:i/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?z.clamp(o,l,n):o},norm:(e,t,a)=>z.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(C=(1664525*C+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(z.rand(e,t+1)),rseed(e){C=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):z.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),z.stroke(n)},rectfill(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),z.fill(n)},circ(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),z.stroke(l)},circfill(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),z.fill(l)},line(e,t,a,l,n){u.beginPath();let i=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~l);u.moveTo(~~e+i,~~t+o),u.lineTo(~~a+i,~~l+o),z.stroke(n)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,l=3,n="normal"){u.font=`${n} ${y}px ${x}`,u.fillStyle=k[~~l%k.length],u.fillText(a,~~e,~~t)},textfont(e){x=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=u;if(n.width=e*i,n.height=t*i,(u=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(u.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&z.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=o,n.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=z.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=k[~~e%k.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=k[~~e%k.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||E,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},use(e,t={}){f?X(e,t):c.push([e,t])},listen:(e,t)=>(T[e=e.toLowerCase()]=T[e]||new Set,T[e].add(t),()=>T[e].delete(t)),emit(e,t,a,l,n){f&&(P("before:"+(e=e.toLowerCase()),t,a,l,n),P(e,t,a,l,n),P("after:"+e,t,a,l,n))},pal(e=a){k=e},def(t,a){z[t]=a,e.global&&(l[t]=a)},timescale(e){g=e},framerate(e){w=1/~~e},stat(t){let a={index:t,value:[e,f,v,p,T,k,E,g,l.zzfxV||1,C,y,x][t]};return z.emit("stat",a),a.value},quit(){for(let e of(cancelAnimationFrame(v),v=0,z.emit("quit"),r))e();if(T={},e.global){for(let e in z)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))z[e]=n[e];function A(){let t=e.loop?e.loop:l;for(let e in T)t[e]&&z.listen(e,t[e]);for(let[e,t]of c)X(e,t);if(e.autoscale&&s(l,"resize",I),e.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=300,o=e=>e.preventDefault(),r=!1;s(d,"mousedown",t=>{if(0===t.button){o(t);let[l,n]=e(t.pageX,t.pageY);z.emit("tap",l,n,0),a(0,l,n),r=!0}}),s(d,"mouseup",a=>{if(0===a.button){o(a);let l=t.get(0),[n,s]=e(a.pageX,a.pageY);i(l)&&z.emit("tapped",l.startX,l.startY,0),z.emit("untap",n,s,0),t.delete(0),r=!1}}),s(d,"mousemove",t=>{o(t);let[a,l]=e(t.pageX,t.pageY);z.def("MX",a),z.def("MY",l),r&&(z.emit("tapping",a,l,0),n(0,a,l))}),s(d,"touchstart",t=>{for(let l of(o(t),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);z.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),s(d,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);z.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let f=e=>{o(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&z.emit("tapped",l.startX,l.startY,e),z.emit("untap",l.x,l.y,e),t.delete(e))};s(d,"touchend",f),s(d,"touchcancel",f),s(l,"blur",()=>{for(let[e,a]of(r=!1,t))z.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;s(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l))}),s(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),s(l,"blur",()=>e.clear()),z.listen("after:update",()=>t.clear()),z.def("iskeydown",t=>a(e,t)),z.def("iskeypressed",e=>a(t,e))}f=!0,z.emit("init",z),m=performance.now(),v=o(S)}function S(t){let a=0;if(e.animate){for(b+=n.min(.2,(t-m)/1e3),m=t;b>=w;)a++,z.emit("update",w*g,a),z.def("T",z.T+w*g),b-=w;v&&(v=o(S))}else a=1;a&&(z.textalign("start","top"),z.emit("draw"))}function I(){let t=e.width||l.innerWidth,a=e.height||e.width||l.innerHeight;z.def("W",d.width=t),z.def("H",d.height=a),z.def("CX",z.W/2),z.def("CY",z.H/2),e.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=n.min(l.innerWidth/z.W,l.innerHeight/z.H),p=(e.pixelart?~~p:p)||1,d.style.width=z.W*p+"px",d.style.height=z.H*p+"px"),(!e.antialias||e.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),z.emit("resized",p),z.cls(0),e.animate||o(S)}function P(e,t,a,l,n){if(T[e])for(let i of T[e])i(t,a,l,n)}function X(e,t){let a=e(z,t);for(let e in a)z.def(e,a[e])}if(e.global){if(l.ENGINE)throw Error("two global litecanvas detected");Object.assign(l,z),l.ENGINE=z}return e.canvas&&(d=document.querySelector(e.canvas)),d=d||document.createElement("canvas"),z.def("CANVAS",d),u=d.getContext("2d"),s(d,"click",()=>l.focus()),d.style="",I(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?s(l,"DOMContentLoaded",()=>o(A)):o(A),z}})();
1
+ (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=globalThis,l=Math,n=2*l.PI,i=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,v=0,x=1,b=0,y=0,k=0)=>{let E=Math,z=2*E.PI,C=c*=500*z/44100/44100,T=n*=(1-l+2*l*E.random(l=[]))*z/44100,I=0,P=0,S=0,A=1,L=0,X=0,Y=0,M=k<0?-1:1,H=z*M*k*2/44100,N=E.cos(H),W=E.sin,D=W(H)/4,F=1+D,V=-2*N/F,q=(1-D)/F,B=(1+M*N)/2/F,O=-(M+N)/F,R=0,G=0,$=0,j=0;for(i=44100*i+9,b*=44100,o*=44100,r*=44100,v*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=i+b+o+r+v|0;S<M;l[S++]=Y*a)++X%(100*w|0)||(Y=s?1<s?2<s?3<s?W(I*I):E.max(E.min(E.tan(I),1),-1):1-(2*I/z%2+2)%2:1-4*E.abs(E.round(I/z)-I/z):W(I),Y=(h?1-y+y*W(z*S/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(S<i?S/i:S<i+b?1-(S-i)/b*(1-x):S<i+b+o?x:S<M-v?(M-S-v)/r*x:0),Y=v?Y/2+(v>S?0:(S<M-v?1:(M-S)/v)*l[S-v|0]/2/a):Y,k&&(Y=j=B*R+O*(R=G)+B*(G=Y)-q*$-V*($=j))),I+=(H=(n+=c+=d)*E.cos(m*P++))+H*g*W(S**5),A&&++A>u&&(n+=p,T+=p,A=0),!h||++L%h||(n=T,c=C,A=A||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(l),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,v=0,x,b="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],C={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},T={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:n,HALF_PI:n/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>l.PI/180*e,rad2deg:e=>180/l.PI*e,round:(e,t=0)=>{if(!t)return l.round(e);let a=10**t;return l.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*l.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?T.clamp(o,l,n):o},norm:(e,t,a)=>T.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(T.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):T.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),T.stroke(n)},rectfill(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),T.fill(n)},circ(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.stroke(l)},circfill(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.fill(l)},line(e,t,a,l,n){u.beginPath();let i=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~l);u.moveTo(~~e+i,~~t+o),u.lineTo(~~a+i,~~l+o),T.stroke(n)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,l=3,n="normal"){u.font=`${n} ${y}px ${b}`,u.fillStyle=E[~~l%E.length],u.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=u;if(n.width=e*i,n.height=t*i,(u=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(u.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&T.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=o,n.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=T.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,l=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?L(e,t):c.push([e,t])},listen:(e,t)=>(C[e=e.toLowerCase()]=C[e]||new Set,C[e].add(t),()=>C[e].delete(t)),emit(e,t,a,l,n){f&&(A("before:"+(e=e.toLowerCase()),t,a,l,n),A(e,t,a,l,n),A("after:"+e,t,a,l,n))},pal(t=e){E=t},def(e,l){T[e]=l,t.global&&(a[e]=l)},timescale(e){g=e},framerate(e){w=1/~~e},stat(e){let l={index:e,value:[t,f,x,p,C,E,z,g,a.zzfxV||1,k,y,b][e]};return T.emit("stat",l),l.value},quit(){for(let e of(cancelAnimationFrame(x),x=0,T.emit("quit"),o))e();if(C={},t.global){for(let e in T)delete a[e];delete a.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))T[e]=l[e];function I(){let e=t.loop?t.loop:a;for(let t in C)e[t]&&T.listen(t,e[t]);for(let[e,t]of c)L(e,t);if(t.autoscale&&r(a,"resize",S),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},i=e=>e&&performance.now()-e.ts<=300,o=e=>e.preventDefault(),s=!1;r(d,"mousedown",t=>{if(0===t.button){o(t);let[a,n]=e(t.pageX,t.pageY);T.emit("tap",a,n,0),l(0,a,n),s=!0}}),r(d,"mouseup",a=>{if(0===a.button){o(a);let l=t.get(0),[n,r]=e(a.pageX,a.pageY);i(l)&&T.emit("tapped",l.startX,l.startY,0),T.emit("untap",n,r,0),t.delete(0),s=!1}}),r(d,"mousemove",t=>{o(t);let[a,l]=e(t.pageX,t.pageY);T.def("MX",a),T.def("MY",l),s&&(T.emit("tapping",a,l,0),n(0,a,l))}),r(d,"touchstart",t=>{for(let a of(o(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);T.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(d,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);T.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let f=e=>{o(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&T.emit("tapped",l.startX,l.startY,e),T.emit("untap",l.x,l.y,e),t.delete(e))};r(d,"touchend",f),r(d,"touchcancel",f),r(a,"blur",()=>{for(let[e,a]of(s=!1,t))T.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;r(a,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l))}),r(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),r(a,"blur",()=>e.clear()),T.listen("after:update",()=>t.clear()),T.def("iskeydown",t=>l(e,t)),T.def("iskeypressed",e=>l(t,e))}f=!0,T.emit("init",T),m=performance.now(),x=i(P)}function P(e){let a=0;if(t.animate){for(v+=l.min(.1,(e-m)/1e3),m=e;v>=w;)a++,T.emit("update",w*g,a),T.def("T",T.T+w*g),v-=w;x&&(x=i(P))}else a=1;a&&(T.textalign("start","top"),T.emit("draw"))}function S(){let e=t.width||a.innerWidth,n=t.height||t.width||a.innerHeight;T.def("W",d.width=e),T.def("H",d.height=n),T.def("CX",T.W/2),T.def("CY",T.H/2),t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=l.min(a.innerWidth/T.W,a.innerHeight/T.H),p=(t.pixelart?~~p:p)||1,d.style.width=T.W*p+"px",d.style.height=T.H*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),T.emit("resized",p),T.cls(0),t.animate||i(P)}function A(e,t,a,l,n){if(C[e])for(let i of C[e])i(t,a,l,n)}function L(e,t){let a=e(T,t);for(let e in a)T.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("two global litecanvas detected");Object.assign(a,T),a.ENGINE=T}return t.canvas&&(d=document.querySelector(t.canvas)),d=d||document.createElement("canvas"),T.def("CANVAS",d),u=d.getContext("2d"),r(d,"click",()=>a.focus()),d.style="",S(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(I)):i(I),T}})();
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.83.4",
3
+ "version": "0.84.0",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
7
7
  "contributors": [],
8
8
  "devDependencies": {
9
- "@swc/core": "^1.12.3",
9
+ "@swc/core": "^1.12.5",
10
10
  "ava": "^6.4.0",
11
11
  "esbuild": "^0.25.5",
12
12
  "gzip-size": "^7.0.0",
13
13
  "prettier": "^3.5.3",
14
- "tap-min": "^3.0.0"
14
+ "tap-min": "^3.0.0",
15
+ "@types/jsdom": "^21.1.7",
16
+ "jsdom": "^26.1.0"
15
17
  },
16
18
  "homepage": "https://litecanvas.github.io/about.html",
17
19
  "repository": {
@@ -26,7 +28,7 @@
26
28
  "types": "types/index.d.ts",
27
29
  "scripts": {
28
30
  "test": "ava --tap | tap-min",
29
- "dev:test": "ava --watch",
31
+ "test:watch": "ava --watch",
30
32
  "dev": "esbuild src/web.js --bundle --watch --outfile=dist/dist.dev.js --servedir=.",
31
33
  "build": "node script/build.js",
32
34
  "gzip-size": "gzip -c dist/dist.min.js | wc -c | xargs printf \" Gzip size: %s bytes\n\"",
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @ts-check
2
- import { zzfx } from './zzfx.js'
2
+ import { setupZzFX } from './zzfx.js'
3
3
  import { defaultPalette } from './palette.js'
4
4
  import { assert } from './dev.js'
5
5
 
@@ -22,6 +22,7 @@ export default function litecanvas(settings = {}) {
22
22
  elem.addEventListener(evt, callback, false)
23
23
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false))
24
24
  },
25
+ zzfx = setupZzFX(root),
25
26
  isNumber = Number.isFinite,
26
27
  /** @type {LitecanvasOptions} */
27
28
  defaults = {
@@ -91,9 +92,6 @@ export default function litecanvas(settings = {}) {
91
92
 
92
93
  /** @type {Omit<LitecanvasInstance,'PI'|'sin'|'cos'|'atan2'|'hypot'|'tan'|'abs'|'ceil'|'floor'|'trunc'|'min'|'max'|'pow'|'sqrt'|'sign'|'exp'|'iskeydown'|'iskeypressed'>} */
93
94
  const instance = {
94
- /** @type {HTMLCanvasElement} */
95
- CANVAS: null,
96
-
97
95
  /** @type {number} */
98
96
  W: 0,
99
97
 
@@ -103,12 +101,6 @@ export default function litecanvas(settings = {}) {
103
101
  /** @type {number} */
104
102
  T: 0,
105
103
 
106
- /** @type {number} */
107
- CX: 0,
108
-
109
- /** @type {number} */
110
- CY: 0,
111
-
112
104
  /** @type {number} */
113
105
  MX: -1,
114
106
 
@@ -251,7 +243,7 @@ export default function litecanvas(settings = {}) {
251
243
  DEV: assert(isNumber(stop1), 'map: 3rd param must be a number')
252
244
  DEV: assert(isNumber(start2), 'map: 4th param must be a number')
253
245
  DEV: assert(isNumber(stop2), 'map: 5th param must be a number')
254
- DEV: assert(max !== min, 'map: the 3rd param must be different than the 2nd param')
246
+ DEV: assert(stop1 !== start1, 'map: the 2nd param must be different than the 3rd param')
255
247
 
256
248
  // prettier-ignore
257
249
  const result = ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2
@@ -921,6 +913,14 @@ export default function litecanvas(settings = {}) {
921
913
  },
922
914
 
923
915
  /** PLUGINS API */
916
+ /**
917
+ * Returns the canvas
918
+ *
919
+ * @returns {HTMLCanvasElement}
920
+ */
921
+ canvas() {
922
+ return _canvas
923
+ },
924
924
  /**
925
925
  * Prepares a plugin to be loaded
926
926
  *
@@ -1405,7 +1405,7 @@ export default function litecanvas(settings = {}) {
1405
1405
 
1406
1406
  if (settings.animate) {
1407
1407
  // prevents too long frames
1408
- _accumulated += math.min(0.2, (now - _lastFrameTime) / 1000)
1408
+ _accumulated += math.min(0.1, (now - _lastFrameTime) / 1000)
1409
1409
  _lastFrameTime = now
1410
1410
 
1411
1411
  while (_accumulated >= _deltaTime) {
package/src/zzfx.js CHANGED
@@ -1,5 +1,13 @@
1
1
  // ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.0 by Frank Force | https://github.com/KilledByAPixel/ZzFX
2
- const zzfxX = /** @__PURE__ */ new AudioContext()
3
-
4
2
  // prettier-ignore
5
- export const zzfx=(i=1,d=.05,z=220,e=0,P=0,S=.1,I=0,c=1,T=0,H=0,V=0,J=0,h=0,j=0,K=0,E=0,r=0,B=1,X=0,L=0,D=0)=>{let n=Math,t=2*n.PI,a=44100,F=T*=500*t/a/a,O=z*=(1-d+2*d*n.random(d=[]))*t/a,x=0,_=0,f=0,g=1,$=0,l=0,o=0,s=D<0?-1:1,u=t*s*D*2/a,G=n.cos(u),C=n.sin,Q=C(u)/4,M=1+Q,m=-2*G/M,y=(1-Q)/M,R=(1+s*G)/2/M,A=-(s+G)/M,v=R,U=0,W=0,Y=0,Z=0;for(e=a*e+9,X*=a,P*=a,S*=a,r*=a,H*=500*t/a**3,K*=t/a,V*=t/a,J*=a,h=a*h|0,i*=.3*(globalThis.zzfxV||1),s=e+X+P+S+r|0;f<s;d[f++]=o*i)++l%(100*E|0)||(o=I?1<I?2<I?3<I?C(x*x):n.max(n.min(n.tan(x),1),-1):1-(2*x/t%2+2)%2:1-4*n.abs(n.round(x/t)-x/t):C(x),o=(h?1-L+L*C(t*f/h):1)*(o<0?-1:1)*n.abs(o)**c*(f<e?f/e:f<e+X?1-(f-e)/X*(1-B):f<e+X+P?B:f<s-r?(s-f-r)/S*B:0),o=r?o/2+(r>f?0:(f<s-r?1:(s-f)/r)*d[f-r|0]/2/i):o,D&&(o=Z=v*U+A*(U=W)+R*(W=o)-y*Y-m*(Y=Z))),u=(z+=T+=H)*n.cos(K*_++),x+=u+u*j*C(f**5),g&&++g>J&&(z+=V,O+=V,g=0),!h||++$%h||(z=O,T=F,g=g||1);i=zzfxX.createBuffer(1,s,a),i.getChannelData(0).set(d),z=zzfxX.createBufferSource(),z.buffer=i,z.connect(zzfxX.destination),z.start()};
3
+ /**
4
+ * @param {typeof globalThis} global
5
+ * @returns {Function} the `zzfx()` function
6
+ */
7
+ export const setupZzFX = (global) => {
8
+ const zzfxX = new AudioContext()
9
+
10
+ global.zzfxV = 1
11
+
12
+ return (i=1,d=.05,z=220,e=0,P=0,S=.1,I=0,c=1,T=0,H=0,V=0,J=0,h=0,j=0,K=0,E=0,r=0,B=1,X=0,L=0,D=0)=>{let n=Math,t=2*n.PI,a=44100,F=T*=500*t/a/a,O=z*=(1-d+2*d*n.random(d=[]))*t/a,x=0,_=0,f=0,g=1,$=0,l=0,o=0,s=D<0?-1:1,u=t*s*D*2/a,G=n.cos(u),C=n.sin,Q=C(u)/4,M=1+Q,m=-2*G/M,y=(1-Q)/M,R=(1+s*G)/2/M,A=-(s+G)/M,v=R,U=0,W=0,Y=0,Z=0;for(e=a*e+9,X*=a,P*=a,S*=a,r*=a,H*=500*t/a**3,K*=t/a,V*=t/a,J*=a,h=a*h|0,i*=.3*global.zzfxV,s=e+X+P+S+r|0;f<s;d[f++]=o*i)++l%(100*E|0)||(o=I?1<I?2<I?3<I?C(x*x):n.max(n.min(n.tan(x),1),-1):1-(2*x/t%2+2)%2:1-4*n.abs(n.round(x/t)-x/t):C(x),o=(h?1-L+L*C(t*f/h):1)*(o<0?-1:1)*n.abs(o)**c*(f<e?f/e:f<e+X?1-(f-e)/X*(1-B):f<e+X+P?B:f<s-r?(s-f-r)/S*B:0),o=r?o/2+(r>f?0:(f<s-r?1:(s-f)/r)*d[f-r|0]/2/i):o,D&&(o=Z=v*U+A*(U=W)+R*(W=o)-y*Y-m*(Y=Z))),u=(z+=T+=H)*n.cos(K*_++),x+=u+u*j*C(f**5),g&&++g>J&&(z+=V,O+=V,g=0),!h||++$%h||(z=O,T=F,g=g||1);i=zzfxX.createBuffer(1,s,a),i.getChannelData(0).set(d),z=zzfxX.createBufferSource(),z.buffer=i,z.connect(zzfxX.destination),z.start()};
13
+ }
package/types/index.d.ts CHANGED
@@ -8,18 +8,12 @@ export default function litecanvas(settings?: LitecanvasOptions): LitecanvasInst
8
8
  declare global {
9
9
  function litecanvas(settings?: LitecanvasOptions): LitecanvasInstance
10
10
 
11
- /** The game canvas HTML element */
12
- var CANVAS: HTMLCanvasElement
13
11
  /** The game screen width */
14
12
  var W: number
15
13
  /** The game screen height */
16
14
  var H: number
17
15
  /** the amount of time (in seconds) since the game started */
18
16
  var T: number
19
- /** the center X of the game screen */
20
- var CX: number
21
- /** the center Y of the game screen */
22
- var CY: number
23
17
  /** The current mouse's horizontal (X) position or -1 (if the mouse was not used or detected) */
24
18
  var MX: number
25
19
  /** The current mouse's vertical (Y) position or -1 (if the mouse was not used or detected) */
@@ -504,6 +498,10 @@ declare global {
504
498
  function iskeypressed(key: string): boolean
505
499
 
506
500
  /** PLUGINS API */
501
+ /**
502
+ * Returns the canvas
503
+ */
504
+ function canvas(): HTMLCanvasElement
507
505
  /**
508
506
  * Prepares a plugin to be loaded
509
507
  *
package/types/types.d.ts CHANGED
@@ -1,16 +1,10 @@
1
1
  type LitecanvasInstance = {
2
- /** The game canvas HTML element */
3
- CANVAS: HTMLCanvasElement
4
2
  /** The game screen width */
5
3
  W: number
6
4
  /** The game screen height */
7
5
  H: number
8
6
  /** the amount of time (in seconds) since the game started */
9
7
  T: number
10
- /** the center X of the game screen */
11
- CX: number
12
- /** the center Y of the game screen */
13
- CY: number
14
8
  /** The current mouse's horizontal (X) position or -1 (if the mouse was not used or detected) */
15
9
  MX: number
16
10
  /** The current mouse's vertical (Y) position or -1 (if the mouse was not used or detected) */
@@ -491,6 +485,10 @@ type LitecanvasInstance = {
491
485
  iskeypressed(key: string): boolean
492
486
 
493
487
  /** PLUGINS API */
488
+ /**
489
+ * Returns the canvas
490
+ */
491
+ canvas(): HTMLCanvasElement
494
492
  /**
495
493
  * Prepares a plugin to be loaded
496
494
  *