@sarmal/core 0.7.1 → 0.8.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/dist/auto-init.js CHANGED
@@ -49,7 +49,7 @@ function resolveCurve(curveDef) {
49
49
  period: curveDef.period ?? TWO_PI,
50
50
  speed: curveDef.speed ?? 1,
51
51
  skeleton: curveDef.skeleton,
52
- skeletonFn: curveDef.skeletonFn
52
+ skeletonFn: curveDef.skeletonFn,
53
53
  };
54
54
  }
55
55
  function createEngine(curveDef, trailLength = 120) {
@@ -75,7 +75,7 @@ function createEngine(curveDef, trailLength = 120) {
75
75
  actualTime += deltaTime;
76
76
  if (morphCurveB !== null && _morphAlpha !== null) {
77
77
  const a = curve.fn(t, actualTime, {});
78
- const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
78
+ const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
79
79
  const b = morphCurveB.fn(tB, actualTime, {});
80
80
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
81
81
  } else {
@@ -99,14 +99,14 @@ function createEngine(curveDef, trailLength = 120) {
99
99
  trail.clear();
100
100
  },
101
101
  seek(newT, { clearTrail = false } = {}) {
102
- t = (newT % curve.period + curve.period) % curve.period;
102
+ t = ((newT % curve.period) + curve.period) % curve.period;
103
103
  if (clearTrail) {
104
104
  trail.clear();
105
105
  }
106
106
  },
107
107
  seekWithTrail(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
108
108
  const advance = curve.speed * step;
109
- const target = (targetT % curve.period + curve.period) % curve.period;
109
+ const target = ((targetT % curve.period) + curve.period) % curve.period;
110
110
  const targetTime = target / curve.speed;
111
111
  t = target;
112
112
  actualTime = targetTime;
@@ -132,13 +132,16 @@ function createEngine(curveDef, trailLength = 120) {
132
132
  ...frozenB,
133
133
  fn: (sampleT, time, params) => {
134
134
  const a = frozenA.fn(sampleT, time, params);
135
- const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
135
+ const tB =
136
+ frozenStrategy === "normalized"
137
+ ? (sampleT / frozenA.period) * frozenB.period
138
+ : sampleT;
136
139
  const b = frozenB.fn(tB, time, params);
137
140
  return {
138
141
  x: a.x + (b.x - a.x) * frozenAlpha,
139
- y: a.y + (b.y - a.y) * frozenAlpha
142
+ y: a.y + (b.y - a.y) * frozenAlpha,
140
143
  };
141
- }
144
+ },
142
145
  };
143
146
  }
144
147
  _morphStrategy = strategy;
@@ -151,7 +154,7 @@ function createEngine(curveDef, trailLength = 120) {
151
154
  completeMorph() {
152
155
  if (morphCurveB !== null) {
153
156
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
154
- t = t / curve.period * morphCurveB.period;
157
+ t = (t / curve.period) * morphCurveB.period;
155
158
  }
156
159
  curve = morphCurveB;
157
160
  }
@@ -163,43 +166,74 @@ function createEngine(curveDef, trailLength = 120) {
163
166
  const points = new Array(steps);
164
167
  if (morphCurveB !== null && _morphAlpha !== null) {
165
168
  for (let i = 0; i < steps; i++) {
166
- const sampleT = i / (steps - 1) * curve.period;
169
+ const sampleT = (i / (steps - 1)) * curve.period;
167
170
  const a = sampleSkeleton(curve, sampleT);
168
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
171
+ const tB =
172
+ _morphStrategy === "normalized"
173
+ ? (sampleT / curve.period) * morphCurveB.period
174
+ : sampleT;
169
175
  const b = sampleSkeleton(morphCurveB, tB);
170
176
  points[i] = {
171
177
  x: a.x + (b.x - a.x) * _morphAlpha,
172
- y: a.y + (b.y - a.y) * _morphAlpha
178
+ y: a.y + (b.y - a.y) * _morphAlpha,
173
179
  };
174
180
  }
175
181
  return points;
176
182
  }
177
183
  for (let i = 0; i < steps; i++) {
178
- const sampleT = i / (steps - 1) * curve.period;
184
+ const sampleT = (i / (steps - 1)) * curve.period;
179
185
  points[i] = sampleSkeleton(curve, sampleT);
180
186
  }
181
187
  return points;
182
- }
188
+ },
183
189
  };
184
190
  }
185
191
 
186
192
  // src/renderer.ts
187
193
  var DEFAULT_MORPH_DURATION_MS = 300;
188
194
  var DEFAULT_HEAD_RADIUS = 4;
189
- var DEFAULT_GLOW_SIZE = 20;
190
195
  var DEFAULT_SKELETON_COLOR = "#ffffff";
191
196
  var DEFAULT_SKELETON_OPACITY = 0.15;
192
197
  var FIT_PADDING = 0.1;
193
- var TRAIL_BATCH_SIZE = 20;
194
198
  var TRAIL_FADE_CURVE = 1.5;
195
199
  var TRAIL_MAX_OPACITY = 0.88;
196
200
  var TRAIL_MIN_WIDTH = 0.5;
197
201
  var TRAIL_MAX_WIDTH = 2.5;
198
- var GLOW_INNER_EDGE = 0.4;
199
- var GLOW_FALLOFF_OPACITY = 0.53;
200
202
  function hexToRgbComponents(hex) {
201
203
  const n = parseInt(hex.slice(1), 16);
202
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
204
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
205
+ }
206
+ function computeTangent(trail, i) {
207
+ const count = trail.length;
208
+ if (count < 2) {
209
+ return { x: 1, y: 0 };
210
+ }
211
+ if (i === 0) {
212
+ const dx2 = trail[1].x - trail[0].x;
213
+ const dy2 = trail[1].y - trail[0].y;
214
+ const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
215
+ return { x: dx2 / len2, y: dy2 / len2 };
216
+ }
217
+ if (i === count - 1) {
218
+ const dx2 = trail[count - 1].x - trail[count - 2].x;
219
+ const dy2 = trail[count - 1].y - trail[count - 2].y;
220
+ const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
221
+ return { x: dx2 / len2, y: dy2 / len2 };
222
+ }
223
+ const dx = trail[i + 1].x - trail[i - 1].x;
224
+ const dy = trail[i + 1].y - trail[i - 1].y;
225
+ const len = Math.sqrt(dx * dx + dy * dy) || 1;
226
+ return { x: dx / len, y: dy / len };
227
+ }
228
+ function computeNormal(trail, i) {
229
+ const tangent = computeTangent(trail, i);
230
+ return { x: -tangent.y, y: tangent.x };
231
+ }
232
+ function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
233
+ target.style.width = `${logicalWidth}px`;
234
+ target.style.height = `${logicalHeight}px`;
235
+ target.width = logicalWidth * dpr;
236
+ target.height = logicalHeight * dpr;
203
237
  }
204
238
  function createRenderer(options) {
205
239
  const canvas = options.canvas;
@@ -213,10 +247,19 @@ function createRenderer(options) {
213
247
  trailColor: options.trailColor ?? "#ffffff",
214
248
  headColor: options.headColor ?? "#ffffff",
215
249
  headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS,
216
- glowSize: options.glowSize ?? DEFAULT_GLOW_SIZE
217
250
  };
218
251
  const trailRgb = hexToRgbComponents(opts.trailColor);
219
- const headRgbFalloff = `rgba(${hexToRgbComponents(opts.headColor)},${GLOW_FALLOFF_OPACITY})`;
252
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
253
+ function setupCanvas() {
254
+ const rect = canvas.getBoundingClientRect();
255
+ const lw = rect.width || 200;
256
+ const lh = rect.height || 200;
257
+ applyDprSizing(canvas, lw, lh, dpr);
258
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
259
+ }
260
+ setupCanvas();
261
+ let logicalWidth = canvas.width / dpr;
262
+ let logicalHeight = canvas.height / dpr;
220
263
  let skeleton = [];
221
264
  let skeletonCanvas = null;
222
265
  let trail = [];
@@ -233,7 +276,10 @@ function createRenderer(options) {
233
276
  function computeBoundaries(pts) {
234
277
  if (pts.length === 0) return null;
235
278
  const first = pts[0];
236
- let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
279
+ let minX = first.x,
280
+ maxX = first.x,
281
+ minY = first.y,
282
+ maxY = first.y;
237
283
  for (const p of pts) {
238
284
  if (p.x < minX) minX = p.x;
239
285
  if (p.x > maxX) maxX = p.x;
@@ -242,17 +288,15 @@ function createRenderer(options) {
242
288
  }
243
289
  const width = maxX - minX;
244
290
  const height = maxY - minY;
245
- const canvasWidth = canvas.width;
246
- const canvasHeight = canvas.height;
247
- const scaleX = canvasWidth / (width * (1 + FIT_PADDING * 2));
248
- const scaleY = canvasHeight / (height * (1 + FIT_PADDING * 2));
291
+ const scaleX = logicalWidth / (width * (1 + FIT_PADDING * 2));
292
+ const scaleY = logicalHeight / (height * (1 + FIT_PADDING * 2));
249
293
  const s = Math.min(scaleX, scaleY);
250
294
  const boundsWidth = width * s;
251
295
  const boundsHeight = height * s;
252
296
  return {
253
297
  scale: s,
254
- offsetX: (canvasWidth - boundsWidth) / 2 - minX * s,
255
- offsetY: (canvasHeight - boundsHeight) / 2 - minY * s
298
+ offsetX: (logicalWidth - boundsWidth) / 2 - minX * s,
299
+ offsetY: (logicalHeight - boundsHeight) / 2 - minY * s,
256
300
  };
257
301
  }
258
302
  function calculateBoundaries() {
@@ -267,6 +311,7 @@ function createRenderer(options) {
267
311
  if (skeleton.length < 2) return;
268
312
  skeletonCanvas = new OffscreenCanvas(canvas.width, canvas.height);
269
313
  const skeletonCtx = skeletonCanvas.getContext("2d");
314
+ skeletonCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
270
315
  skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
271
316
  skeletonCtx.lineWidth = 1.5;
272
317
  skeletonCtx.beginPath();
@@ -312,32 +357,41 @@ function createRenderer(options) {
312
357
  }
313
358
  ctx.stroke();
314
359
  } else if (skeletonCanvas) {
315
- ctx.drawImage(skeletonCanvas, 0, 0);
360
+ ctx.drawImage(skeletonCanvas, 0, 0, logicalWidth, logicalHeight);
316
361
  }
317
362
  }
318
363
  function drawTrail() {
319
364
  if (trailCount < 2) {
320
365
  return;
321
366
  }
322
- ctx.lineJoin = "round";
323
- ctx.lineCap = "round";
324
- for (let batchIndex = 0; batchIndex < trailCount - 1; batchIndex += TRAIL_BATCH_SIZE) {
325
- const bEnd = Math.min(batchIndex + TRAIL_BATCH_SIZE, trailCount - 1);
326
- const progress = (batchIndex + bEnd) / 2 / (trailCount - 1);
367
+ for (let i = 0; i < trailCount - 1; i++) {
368
+ const progress = i / (trailCount - 1);
369
+ const nextProgress = (i + 1) / (trailCount - 1);
327
370
  const alpha = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
328
- const lineWidth = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
371
+ const width = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
372
+ const nextWidth = TRAIL_MIN_WIDTH + nextProgress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
373
+ const curr = trail[i];
374
+ const next = trail[i + 1];
375
+ const n0 = computeNormal(trail, i);
376
+ const n1 = computeNormal(trail, i + 1);
377
+ const halfW0 = width / 2;
378
+ const halfW1 = nextWidth / 2;
379
+ const l0x = curr.x * scale + offsetX + n0.x * halfW0;
380
+ const l0y = curr.y * scale + offsetY + n0.y * halfW0;
381
+ const r0x = curr.x * scale + offsetX - n0.x * halfW0;
382
+ const r0y = curr.y * scale + offsetY - n0.y * halfW0;
383
+ const l1x = next.x * scale + offsetX + n1.x * halfW1;
384
+ const l1y = next.y * scale + offsetY + n1.y * halfW1;
385
+ const r1x = next.x * scale + offsetX - n1.x * halfW1;
386
+ const r1y = next.y * scale + offsetY - n1.y * halfW1;
387
+ ctx.fillStyle = `rgba(${trailRgb},${alpha})`;
329
388
  ctx.beginPath();
330
- for (let i = batchIndex; i <= bEnd; i++) {
331
- const point = trail[i];
332
- if (i === batchIndex) {
333
- ctx.moveTo(point.x * scale + offsetX, point.y * scale + offsetY);
334
- } else {
335
- ctx.lineTo(point.x * scale + offsetX, point.y * scale + offsetY);
336
- }
337
- }
338
- ctx.strokeStyle = `rgba(${trailRgb},${alpha})`;
339
- ctx.lineWidth = lineWidth;
340
- ctx.stroke();
389
+ ctx.moveTo(l0x, l0y);
390
+ ctx.lineTo(l1x, l1y);
391
+ ctx.lineTo(r1x, r1y);
392
+ ctx.lineTo(r0x, r0y);
393
+ ctx.closePath();
394
+ ctx.fill();
341
395
  }
342
396
  }
343
397
  function drawHead() {
@@ -346,14 +400,6 @@ function createRenderer(options) {
346
400
  }
347
401
  const x = head.x * scale + offsetX;
348
402
  const y = head.y * scale + offsetY;
349
- const gradient = ctx.createRadialGradient(x, y, 0, x, y, opts.glowSize);
350
- gradient.addColorStop(0, opts.headColor);
351
- gradient.addColorStop(GLOW_INNER_EDGE, headRgbFalloff);
352
- gradient.addColorStop(1, "transparent");
353
- ctx.fillStyle = gradient;
354
- ctx.beginPath();
355
- ctx.arc(x, y, opts.glowSize, 0, Math.PI * 2);
356
- ctx.fill();
357
403
  ctx.fillStyle = opts.headColor;
358
404
  ctx.beginPath();
359
405
  ctx.arc(x, y, opts.headRadius, 0, Math.PI * 2);
@@ -387,7 +433,7 @@ function createRenderer(options) {
387
433
  trail = engine.tick(deltaTime);
388
434
  trailCount = engine.trailCount;
389
435
  head = trailCount > 0 ? trail[trailCount - 1] : null;
390
- ctx.clearRect(0, 0, canvas.width, canvas.height);
436
+ ctx.clearRect(0, 0, logicalWidth, logicalHeight);
391
437
  if (engine.isLiveSkeleton && engine.morphAlpha === null) {
392
438
  skeleton = engine.getSarmalSkeleton();
393
439
  calculateBoundaries();
@@ -447,33 +493,36 @@ function createRenderer(options) {
447
493
  return new Promise((resolve) => {
448
494
  morphResolve = resolve;
449
495
  });
450
- }
496
+ },
451
497
  };
452
498
  }
453
499
 
454
500
  // src/curves.ts
455
501
  var TWO_PI2 = Math.PI * 2;
456
502
  function artemis2(t, _time, _params) {
457
- const a = 0.35, b = 0.15, ox = 0.175;
458
- const s = Math.sin(t), c = Math.cos(t);
503
+ const a = 0.35,
504
+ b = 0.15,
505
+ ox = 0.175;
506
+ const s = Math.sin(t),
507
+ c = Math.cos(t);
459
508
  const denom = 1 + s * s;
460
509
  return {
461
- x: c * (1 + a * c) / denom - ox,
462
- y: s * c * (1 + b * c) / denom
510
+ x: (c * (1 + a * c)) / denom - ox,
511
+ y: (s * c * (1 + b * c)) / denom,
463
512
  };
464
513
  }
465
514
  function epitrochoid7(t, _time, _params) {
466
515
  const d = 1 + 0.55 * Math.sin(t * 0.5);
467
516
  return {
468
517
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
469
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
518
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
470
519
  };
471
520
  }
472
521
  function epitrochoid7Skeleton(t) {
473
522
  const d = 1.275;
474
523
  return {
475
524
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
476
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
525
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
477
526
  };
478
527
  }
479
528
  function astroid(t, _time, _params) {
@@ -481,55 +530,56 @@ function astroid(t, _time, _params) {
481
530
  const s = Math.sin(t);
482
531
  return {
483
532
  x: c * c * c,
484
- y: s * s * s
533
+ y: s * s * s,
485
534
  };
486
535
  }
487
536
  function deltoid(t, _time, _params) {
488
537
  return {
489
538
  x: 2 * Math.cos(t) + Math.cos(2 * t),
490
- y: 2 * Math.sin(t) - Math.sin(2 * t)
539
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
491
540
  };
492
541
  }
493
542
  function rose5(t, _time, _params) {
494
543
  const r = Math.cos(5 * t);
495
544
  return {
496
545
  x: r * Math.cos(t),
497
- y: r * Math.sin(t)
546
+ y: r * Math.sin(t),
498
547
  };
499
548
  }
500
549
  function rose3(t, _time, _params) {
501
550
  const r = Math.cos(3 * t);
502
551
  return {
503
552
  x: r * Math.cos(t),
504
- y: r * Math.sin(t)
553
+ y: r * Math.sin(t),
505
554
  };
506
555
  }
507
556
  function lissajous32(t, time, _params) {
508
557
  const phi = time * 0.45;
509
558
  return {
510
559
  x: Math.sin(3 * t + phi),
511
- y: Math.sin(2 * t)
560
+ y: Math.sin(2 * t),
512
561
  };
513
562
  }
514
563
  function lissajous43(t, time, _params) {
515
564
  const phi = time * 0.38;
516
565
  return {
517
566
  x: Math.sin(4 * t + phi),
518
- y: Math.sin(3 * t)
567
+ y: Math.sin(3 * t),
519
568
  };
520
569
  }
521
570
  function epicycloid3(t, _time, _params) {
522
571
  return {
523
572
  x: 4 * Math.cos(t) - Math.cos(4 * t),
524
- y: 4 * Math.sin(t) - Math.sin(4 * t)
573
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
525
574
  };
526
575
  }
527
576
  function lame(t, time, _params) {
528
577
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
529
- const c = Math.cos(t), s = Math.sin(t);
578
+ const c = Math.cos(t),
579
+ s = Math.sin(t);
530
580
  return {
531
581
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
532
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
582
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
533
583
  };
534
584
  }
535
585
  var curves = {
@@ -537,66 +587,66 @@ var curves = {
537
587
  name: "Artemis II",
538
588
  fn: artemis2,
539
589
  period: TWO_PI2,
540
- speed: 0.7
590
+ speed: 0.7,
541
591
  },
542
592
  epitrochoid7: {
543
593
  name: "Epitrochoid",
544
594
  fn: epitrochoid7,
545
595
  period: TWO_PI2,
546
596
  speed: 1.4,
547
- skeletonFn: epitrochoid7Skeleton
597
+ skeletonFn: epitrochoid7Skeleton,
548
598
  },
549
599
  astroid: {
550
600
  name: "Astroid",
551
601
  fn: astroid,
552
602
  period: TWO_PI2,
553
- speed: 1.1
603
+ speed: 1.1,
554
604
  },
555
605
  deltoid: {
556
606
  name: "Deltoid",
557
607
  fn: deltoid,
558
608
  period: TWO_PI2,
559
- speed: 0.9
609
+ speed: 0.9,
560
610
  },
561
611
  rose5: {
562
612
  name: "Rose (n=5)",
563
613
  fn: rose5,
564
614
  period: TWO_PI2,
565
- speed: 1
615
+ speed: 1,
566
616
  },
567
617
  rose3: {
568
618
  name: "Rose (n=3)",
569
619
  fn: rose3,
570
620
  period: TWO_PI2,
571
- speed: 1.15
621
+ speed: 1.15,
572
622
  },
573
623
  lissajous32: {
574
624
  name: "Lissajous 3:2",
575
625
  fn: lissajous32,
576
626
  period: TWO_PI2,
577
627
  speed: 2,
578
- skeleton: "live"
628
+ skeleton: "live",
579
629
  },
580
630
  lissajous43: {
581
631
  name: "Lissajous 4:3",
582
632
  fn: lissajous43,
583
633
  period: TWO_PI2,
584
634
  speed: 1.8,
585
- skeleton: "live"
635
+ skeleton: "live",
586
636
  },
587
637
  epicycloid3: {
588
638
  name: "Epicycloid (n=3)",
589
639
  fn: epicycloid3,
590
640
  period: TWO_PI2,
591
- speed: 0.75
641
+ speed: 0.75,
592
642
  },
593
643
  lame: {
594
644
  name: "Lam\xE9 Curve",
595
645
  fn: lame,
596
646
  period: TWO_PI2,
597
647
  speed: 1,
598
- skeleton: "live"
599
- }
648
+ skeleton: "live",
649
+ },
600
650
  };
601
651
 
602
652
  // src/index.ts
@@ -619,12 +669,12 @@ function init() {
619
669
  return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
620
670
  }
621
671
  const sarmal = createSarmal(canvas, curveDef, {
622
- ...canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor },
623
- ...canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor },
624
- ...canvas.dataset.headColor && { headColor: canvas.dataset.headColor },
625
- ...canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) },
626
- ...canvas.dataset.glowSize && { glowSize: parseInt(canvas.dataset.glowSize, 10) },
627
- ...canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }
672
+ ...(canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor }),
673
+ ...(canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor }),
674
+ ...(canvas.dataset.headColor && { headColor: canvas.dataset.headColor }),
675
+ ...(canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) }),
676
+ ...(canvas.dataset.glowSize && { glowSize: parseInt(canvas.dataset.glowSize, 10) }),
677
+ ...(canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }),
628
678
  });
629
679
  sarmal.start();
630
680
  });
@@ -635,4 +685,4 @@ if (document.readyState === "loading") {
635
685
  init();
636
686
  }
637
687
  //# sourceMappingURL=auto-init.js.map
638
- //# sourceMappingURL=auto-init.js.map
688
+ //# sourceMappingURL=auto-init.js.map