fragment-tools 0.2.12 → 0.2.14

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.
Files changed (55) hide show
  1. package/package.json +12 -11
  2. package/src/cli/build.js +1 -0
  3. package/src/cli/create.js +22 -4
  4. package/src/cli/createConfig.js +8 -3
  5. package/src/cli/getEntries.js +10 -1
  6. package/src/cli/plugins/hot-shader-replacement.js +54 -16
  7. package/src/cli/plugins/save.js +97 -38
  8. package/src/cli/prompts.js +89 -36
  9. package/src/cli/run.js +1 -1
  10. package/src/client/app/actions/resize.js +8 -1
  11. package/src/client/app/attachments/draggable.js +93 -0
  12. package/src/client/app/client.js +90 -18
  13. package/src/client/app/components/IconFlip.svelte +46 -0
  14. package/src/client/app/hooks.js +25 -1
  15. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +95 -3
  16. package/src/client/app/lib/canvas-recorder/FrameRecorder.js +45 -3
  17. package/src/client/app/lib/canvas-recorder/GIFRecorder.js +72 -13
  18. package/src/client/app/lib/canvas-recorder/MediaBunnyRecorder.js +43 -9
  19. package/src/client/app/lib/canvas-recorder/utils.js +18 -9
  20. package/src/client/app/renderers/2DRenderer.js +20 -16
  21. package/src/client/app/renderers/FragmentRenderer.js +1 -1
  22. package/src/client/app/renderers/P5GLRenderer.js +14 -6
  23. package/src/client/app/renderers/P5Renderer.js +9 -1
  24. package/src/client/app/renderers/THREERenderer.js +62 -48
  25. package/src/client/app/state/Sketch.svelte.js +149 -9
  26. package/src/client/app/state/errors.svelte.js +19 -0
  27. package/src/client/app/state/exports.svelte.js +14 -1
  28. package/src/client/app/state/rendering.svelte.js +47 -0
  29. package/src/client/app/state/sketches.svelte.js +43 -7
  30. package/src/client/app/state/utils.svelte.js +49 -0
  31. package/src/client/app/ui/Field.svelte +6 -1
  32. package/src/client/app/ui/FieldSection.svelte +4 -4
  33. package/src/client/app/ui/ParamsOutput.svelte +1 -1
  34. package/src/client/app/ui/SketchRenderer.svelte +16 -0
  35. package/src/client/app/ui/fields/ButtonInput.svelte +2 -0
  36. package/src/client/app/ui/fields/CheckboxInput.svelte +13 -11
  37. package/src/client/app/ui/fields/ColorInput.svelte +16 -11
  38. package/src/client/app/ui/fields/GradientInput.svelte +607 -0
  39. package/src/client/app/ui/fields/Input.svelte +10 -6
  40. package/src/client/app/ui/fields/IntervalInput.svelte +27 -35
  41. package/src/client/app/ui/fields/NumberInput.svelte +51 -13
  42. package/src/client/app/ui/fields/PaletteInput.svelte +181 -0
  43. package/src/client/app/ui/fields/ProgressInput.svelte +44 -16
  44. package/src/client/app/ui/fields/TextareaInput.svelte +10 -10
  45. package/src/client/app/utils/canvas.utils.js +105 -28
  46. package/src/client/app/utils/color.utils.js +74 -17
  47. package/src/client/app/utils/fields.utils.js +68 -26
  48. package/src/client/app/utils/file.utils.js +86 -31
  49. package/src/client/app/utils/glsl.utils.js +11 -2
  50. package/src/client/app/utils/glslErrors.js +31 -21
  51. package/src/client/app/utils/index.js +28 -12
  52. package/src/client/main.js +7 -1
  53. package/src/types/global.d.ts +143 -0
  54. package/src/types/props.d.ts +40 -15
  55. package/tsconfig.json +1 -1
@@ -1,6 +1,47 @@
1
+ /**
2
+ * @callback CanvasRecorderStartCallback
3
+ * @returns {void}
4
+ */
5
+
6
+ /**
7
+ * @callback CanvasRecorderTickCallback
8
+ * @param {TickData} data - Tick data
9
+ * @returns {void}
10
+ */
11
+
12
+ /**
13
+ * @callback CanvasRecorderCompleteCallback
14
+ * @param {Blob | Blob[] | null} result
15
+ * @returns {void}
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} TickData
20
+ * @property {number} time - Current time in milliseconds
21
+ * @property {number} deltaTime - Time since last frame in milliseconds
22
+ * @property {number} frameCount - Current frame count
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} CanvasRecorderOptions
27
+ * @property {number} [duration=Infinity] - Recording duration in seconds
28
+ * @property {number} [framerate=25] - Frames per second
29
+ * @property {number} [quality=100] - Recording quality (1-100)
30
+ * @property {string} format - Output format
31
+ * @property {CanvasRecorderStartCallback} [onStart] - Callback when recording starts
32
+ * @property {CanvasRecorderTickCallback} [onTick] - Callback on each frame
33
+ * @property {CanvasRecorderCompleteCallback} [onComplete] - Callback when recording completes
34
+ */
35
+
36
+ /** @type {CanvasRecorderStartCallback} */
1
37
  let noop = () => {};
2
38
 
3
39
  class CanvasRecorder {
40
+ /**
41
+ * Create a canvas recorder
42
+ * @param {HTMLCanvasElement} canvas - The canvas to record
43
+ * @param {CanvasRecorderOptions} options - Recording options
44
+ */
4
45
  constructor(
5
46
  canvas,
6
47
  {
@@ -13,30 +54,62 @@ class CanvasRecorder {
13
54
  onComplete = noop,
14
55
  },
15
56
  ) {
57
+ /** @type {HTMLCanvasElement} */
16
58
  this.canvas = canvas;
59
+ /** @type {number} */
17
60
  this.framerate = framerate;
61
+ /** @type {number} */
18
62
  this.duration = duration;
63
+ /** @type {number} */
19
64
  this.quality = quality;
65
+ /** @type {string} */
20
66
  this.format = format;
67
+ /** @type {CanvasRecorderStartCallback} */
21
68
  this.onStart = onStart;
69
+ /** @type {CanvasRecorderTickCallback} */
22
70
  this.onTick = onTick;
71
+ /** @type {Function} */
23
72
  this.onComplete = onComplete;
24
-
73
+ /** @type {number} */
25
74
  this.time = 0;
75
+
76
+ /** @type {number} */
26
77
  this.deltaTime = 1000 / this.framerate;
27
78
 
79
+ /** @type {number} */
28
80
  this.frameDuration = 1000 / this.framerate;
81
+
82
+ /** @type {number} */
29
83
  this.frameTotal = isFinite(this.duration)
30
84
  ? this.duration * this.framerate
31
85
  : Infinity;
86
+
87
+ /** @type {boolean} */
32
88
  this.started = false;
89
+
90
+ /** @type {boolean} */
33
91
  this.stopped = false;
34
92
 
93
+ /** @type {number} */
35
94
  this.startTime = 0;
95
+
96
+ /** @type {number} */
97
+ this.frameCount = 0;
98
+
99
+ /** @type {Blob | Blob[] | null} */
100
+ this.result = null;
36
101
  }
37
102
 
103
+ /**
104
+ * Load resources before recording (override in subclass)
105
+ * @returns {Promise<void>}
106
+ */
38
107
  async load() {}
39
108
 
109
+ /**
110
+ * Start the recording
111
+ * @returns {Promise<void>}
112
+ */
40
113
  async start() {
41
114
  this.startTime = performance.now();
42
115
  this.onStart();
@@ -65,11 +138,17 @@ class CanvasRecorder {
65
138
  this._tick();
66
139
  }
67
140
 
141
+ /**
142
+ * Internal tick handler
143
+ * @private
144
+ * @returns {Promise<void>}
145
+ */
68
146
  async _tick() {
69
147
  console.log(`CanvasRecorder - render frame ${this.frameCount + 1}`);
70
148
  this.onTick({
71
149
  time: this.time,
72
150
  deltaTime: this.deltaTime,
151
+ frameCount: this.frameCount,
73
152
  });
74
153
 
75
154
  await this.tick({
@@ -98,8 +177,17 @@ class CanvasRecorder {
98
177
  }
99
178
  }
100
179
 
101
- tick() {}
102
-
180
+ /**
181
+ * Process a single frame (override in subclass)
182
+ * @param {TickData} _data - Frame data
183
+ * @returns {Promise<void>}
184
+ */
185
+ async tick(_data) {}
186
+
187
+ /**
188
+ * End the recording and compile result
189
+ * @returns {void}
190
+ */
103
191
  end() {
104
192
  console.log(
105
193
  `CanvasRecorder - compiled ${this.frameCount + 1} frames in ${(performance.now() - this.startTime) / 1000}s`,
@@ -107,6 +195,10 @@ class CanvasRecorder {
107
195
  this.onComplete(this.result);
108
196
  }
109
197
 
198
+ /**
199
+ * Stop the recording
200
+ * @returns {void}
201
+ */
110
202
  stop() {
111
203
  this.stopped = true;
112
204
  }
@@ -3,24 +3,62 @@ import { map } from '../../utils/math.utils';
3
3
  import CanvasRecorder from './CanvasRecorder';
4
4
  import { exportCanvas } from './utils';
5
5
 
6
+ /**
7
+ * @typedef {Object} FrameRecorderOptions
8
+ * @property {string} [imageEncoding='png'] - Image encoding format (png, jpeg, webp)
9
+ * @property {number} [duration] - Recording duration in seconds
10
+ * @property {number} [framerate] - Frames per second
11
+ * @property {number} [quality] - Recording quality (1-100)
12
+ * @property {string} format - Output format
13
+ * @property {import('./CanvasRecorder').CanvasRecorderStartCallback} [onStart] - Callback when recording starts
14
+ * @property {import('./CanvasRecorder').CanvasRecorderTickCallback} [onTick] - Callback on each frame
15
+ * @property {import('./CanvasRecorder').CanvasRecorderCompleteCallback} [onComplete] - Callback when recording completes
16
+ */
17
+
18
+ /**
19
+ * Recorder that captures individual frames as images
20
+ * @extends CanvasRecorder
21
+ */
6
22
  class FrameRecorder extends CanvasRecorder {
23
+ /**
24
+ * Create a frame recorder
25
+ * @param {HTMLCanvasElement} canvas - The canvas to record
26
+ * @param {FrameRecorderOptions} options - Recording options
27
+ */
7
28
  constructor(canvas, options) {
8
29
  super(canvas, options);
9
30
 
10
31
  const { imageEncoding = 'png' } = options;
11
32
 
33
+ /** @type {string} */
12
34
  this.imageEncoding = imageEncoding;
13
35
 
36
+ /** @type {number} */
14
37
  this.imageQuality = map(this.quality, 1, 100, 0, 1);
38
+
39
+ /** @type {string[]} */
40
+ this.frames = [];
41
+
42
+ /** @type {Blob[]} */
43
+ this.result = [];
15
44
  }
16
45
 
17
- start() {
46
+ /**
47
+ * Start recording frames
48
+ * @returns {Promise<void>}
49
+ */
50
+ async start() {
18
51
  this.frames = [];
19
52
 
20
- super.start();
53
+ await super.start();
21
54
  }
22
55
 
23
- tick() {
56
+ /**
57
+ * Capture a single frame
58
+ * @param {import('./CanvasRecorder').TickData} _data - Frame data (unused)
59
+ * @returns {Promise<void>}
60
+ */
61
+ async tick(_data) {
24
62
  let { dataURL } = exportCanvas(this.canvas, {
25
63
  encoding: `image/${this.imageEncoding}`,
26
64
  encodingQuality: this.imageQuality,
@@ -29,6 +67,10 @@ class FrameRecorder extends CanvasRecorder {
29
67
  this.frames[this.frameCount] = dataURL;
30
68
  }
31
69
 
70
+ /**
71
+ * End recording and convert frames to blobs
72
+ * @returns {Promise<void>}
73
+ */
32
74
  async end() {
33
75
  this.result = await Promise.all(
34
76
  this.frames.map((dataURL) => createBlobFromDataURL(dataURL)),
@@ -2,8 +2,41 @@ import { map } from '../../utils/math.utils';
2
2
  import { GIFEncoder, quantize, applyPalette } from 'gifenc';
3
3
  import CanvasRecorder from './CanvasRecorder';
4
4
 
5
+ /**
6
+ * @typedef {import('./CanvasRecorder').CanvasRecorderOptions} GIFRecorderOptions
7
+ */
8
+
9
+ /**
10
+ * Recorder that captures frames and encodes them as an animated GIF
11
+ * @extends CanvasRecorder
12
+ */
5
13
  class GIFRecorder extends CanvasRecorder {
6
- start() {
14
+ /**
15
+ * Create a GIF recorder
16
+ * @param {HTMLCanvasElement} canvas - The canvas to record
17
+ * @param {GIFRecorderOptions} options - Recording options
18
+ */
19
+ constructor(canvas, options) {
20
+ super(canvas, options);
21
+
22
+ /** @type {ReturnType<typeof GIFEncoder> | null} */
23
+ this.encoder = null;
24
+
25
+ /** @type {HTMLCanvasElement} */
26
+ this.tmpCanvas = document.createElement('canvas');
27
+
28
+ /** @type {CanvasRenderingContext2D | null} */
29
+ this.tmpContext = this.tmpCanvas.getContext('2d');
30
+
31
+ /** @type {number} */
32
+ this.maxColors = 256;
33
+ }
34
+
35
+ /**
36
+ * Start GIF recording
37
+ * @returns {Promise<void>}
38
+ */
39
+ async start() {
7
40
  this.encoder = GIFEncoder();
8
41
 
9
42
  this.tmpCanvas = document.createElement('canvas');
@@ -21,34 +54,60 @@ class GIFRecorder extends CanvasRecorder {
21
54
  : Infinity;
22
55
  }
23
56
 
24
- super.start();
57
+ await super.start();
25
58
  }
26
59
 
60
+ /**
61
+ * Get RGBA pixel data from a bitmap
62
+ * @param {HTMLCanvasElement | ImageBitmap} bitmap - The bitmap to extract pixels from
63
+ * @param {number} [width=bitmap.width] - Target width
64
+ * @param {number} [height=bitmap.height] - Target height
65
+ * @returns {Uint8ClampedArray} RGBA pixel data
66
+ */
27
67
  getBitmapRGBA(bitmap, width = bitmap.width, height = bitmap.height) {
28
68
  this.tmpCanvas.width = width;
29
69
  this.tmpCanvas.height = height;
30
- this.tmpContext.clearRect(0, 0, width, height);
31
- this.tmpContext.drawImage(bitmap, 0, 0, width, height);
32
- return this.tmpContext.getImageData(0, 0, width, height).data;
70
+
71
+ if (this.tmpContext) {
72
+ this.tmpContext.clearRect(0, 0, width, height);
73
+ this.tmpContext.drawImage(bitmap, 0, 0, width, height);
74
+ return this.tmpContext.getImageData(0, 0, width, height).data;
75
+ }
76
+
77
+ return new Uint8ClampedArray();
33
78
  }
34
79
 
35
- tick() {
80
+ /**
81
+ * Capture and encode a single frame
82
+ * @param {import('./CanvasRecorder').TickData} _data - Frame data (unused)
83
+ * @returns {Promise<void>}
84
+ */
85
+ async tick(_data) {
36
86
  const { width, height } = this.canvas;
37
87
 
38
88
  const pixels = this.getBitmapRGBA(this.canvas, width, height);
39
89
  const palette = quantize(pixels, this.maxColors);
40
90
  const index = applyPalette(pixels, palette);
41
91
 
42
- this.encoder.writeFrame(index, width, height, {
43
- palette: palette,
44
- delay: this.frameDuration,
45
- });
92
+ if (this.encoder) {
93
+ this.encoder.writeFrame(index, width, height, {
94
+ palette: palette,
95
+ delay: this.frameDuration,
96
+ });
97
+ }
46
98
  }
47
99
 
100
+ /**
101
+ * End recording and create GIF blob
102
+ * @returns {void}
103
+ */
48
104
  end() {
49
- this.encoder.finish();
50
-
51
- this.result = new Blob([this.encoder.bytes()], { type: 'image/gif' });
105
+ if (this.encoder) {
106
+ this.encoder.finish();
107
+ this.result = new Blob([this.encoder.bytes()], {
108
+ type: 'image/gif',
109
+ });
110
+ }
52
111
 
53
112
  super.end();
54
113
  }
@@ -4,7 +4,6 @@ import {
4
4
  Mp4OutputFormat,
5
5
  MkvOutputFormat,
6
6
  MovOutputFormat,
7
- WebMInputFormat,
8
7
  BufferTarget,
9
8
  CanvasSource,
10
9
  Quality,
@@ -18,6 +17,26 @@ import {
18
17
  import { map } from '@fragment/utils/math.utils.js';
19
18
  import { VIDEO_FORMATS } from '@fragment/state/exports.svelte.js';
20
19
 
20
+ /**
21
+ * @typedef {'avc' | 'hevc' | 'vp9' | 'av1' | 'vp8'} VideoCodec
22
+ */
23
+
24
+ /**
25
+ * @typedef {Object} MediaBunnyRecorderOptions
26
+ * @property {VideoCodec} codec - Video codec to use
27
+ * @property {number} [duration] - Recording duration in seconds
28
+ * @property {number} [framerate] - Frames per second
29
+ * @property {number} [quality] - Recording quality (1-100)
30
+ * @property {string} format - Output format
31
+ * @property {import('./CanvasRecorder').CanvasRecorderStartCallback} [onStart] - Callback when recording starts
32
+ * @property {import('./CanvasRecorder').CanvasRecorderTickCallback} [onTick] - Callback on each frame
33
+ * @property {import('./CanvasRecorder').CanvasRecorderCompleteCallback} [onComplete] - Callback when recording completes
34
+ */
35
+
36
+ /**
37
+ * Recorder that uses MediaBunny to encode video
38
+ * @extends CanvasRecorder
39
+ */
21
40
  class MediaBunnyRecorder extends CanvasRecorder {
22
41
  /** @type Quality[] */
23
42
  static BITRATES = [
@@ -29,15 +48,14 @@ class MediaBunnyRecorder extends CanvasRecorder {
29
48
  ];
30
49
 
31
50
  /**
32
- *
33
- * @param {HTMLCanvasElement} canvas
34
- * @param {object} options
35
- * @param {codec} options.string
51
+ * Create a MediaBunny recorder
52
+ * @param {HTMLCanvasElement} canvas - The canvas to record
53
+ * @param {MediaBunnyRecorderOptions} options - Recording options
36
54
  */
37
55
  constructor(canvas, { codec, ...options }) {
38
56
  super(canvas, options);
39
57
 
40
- /** @type {string} */
58
+ /** @type {VideoCodec} */
41
59
  this.codec = codec;
42
60
 
43
61
  const outputFormats = new Map();
@@ -72,23 +90,39 @@ class MediaBunnyRecorder extends CanvasRecorder {
72
90
  });
73
91
  }
74
92
 
93
+ /**
94
+ * Load and start the output
95
+ * @returns {Promise<void>}
96
+ */
75
97
  async load() {
76
98
  await this.output.start();
77
99
  }
78
100
 
79
- async tick({ frameCount, time }) {
101
+ /**
102
+ * Process a single frame
103
+ * @param {import('./CanvasRecorder').TickData} tickData - Frame data
104
+ * @returns {Promise<void>}
105
+ */
106
+ async tick({ frameCount }) {
80
107
  const timestamp = frameCount / this.framerate;
81
108
 
82
109
  this.videoSource.add(timestamp, this.frameDuration / 1000);
83
110
  }
84
111
 
112
+ /**
113
+ * End recording and create video blob
114
+ * @returns {Promise<void>}
115
+ */
85
116
  async end() {
86
117
  await this.output.finalize();
87
118
 
88
119
  const { mimeType } = this.output.format;
89
- const { buffer } = this.output.target;
120
+ const target = /** @type {BufferTarget} */ (this.output.target);
121
+ const buffer = target.buffer;
90
122
 
91
- this.result = new Blob([buffer], { type: mimeType });
123
+ if (buffer) {
124
+ this.result = new Blob([buffer], { type: mimeType });
125
+ }
92
126
 
93
127
  super.end();
94
128
  }
@@ -3,15 +3,24 @@ import { changeDpiDataUrl } from 'changedpi';
3
3
  const supportedEncodings = ['image/png', 'image/jpeg', 'image/webp'];
4
4
 
5
5
  /**
6
- * Create a Data URL from a canvas
7
- * @param {HTMLCanvasElement} canvas
8
- * @param {object} [options]
9
- * @param {string} [encoding="image/png"]
10
- * @param {number} [encodingQuality=0.92]
11
- * @param {number} [pixelsPerInch=72]
12
- * @returns {object} result
13
- * @returns {string} result.dataURL
14
- * @returns {string} result.extension
6
+ * @typedef {Object} ExportCanvasOptions
7
+ * @property {string} [encoding='image/png'] - Image MIME type (image/png, image/jpeg, image/webp)
8
+ * @property {number} [encodingQuality=0.92] - Image quality (0-1)
9
+ * @property {number} [pixelsPerInch=72] - DPI/PPI for the exported image
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} ExportCanvasResult
14
+ * @property {string} extension - File extension (e.g., '.png', '.jpg')
15
+ * @property {string} dataURL - Data URL of the exported canvas
16
+ */
17
+
18
+ /**
19
+ * Export a canvas to a data URL with specified encoding and DPI
20
+ * @param {HTMLCanvasElement} canvas - The canvas to export
21
+ * @param {ExportCanvasOptions} [options={}] - Export options
22
+ * @returns {ExportCanvasResult}
23
+ * @throws {Error} If encoding is not supported
15
24
  */
16
25
  export function exportCanvas(
17
26
  canvas,
@@ -5,32 +5,36 @@
5
5
  */
6
6
 
7
7
  /**
8
- * @param {object} params
9
- * @param {number} params.id
10
- * @param {HTMLCanvasElement} params.canvas
11
- * @param {HTMLElement} params.container
12
- * @param {number} params.width
13
- * @param {number} params.height
14
- * @param {number} params.pixelRatio
8
+ * @typedef {object} PreviewParams
9
+ * @property {number} id
10
+ * @property {HTMLCanvasElement} canvas
11
+ * @property {HTMLElement} container
12
+ * @property {number} width
13
+ * @property {number} height
14
+ * @property {number} pixelRatio
15
+ */
16
+
17
+ /**
18
+ * @param {PreviewParams} params
15
19
  * @returns {MountParams2DRenderer}
16
20
  */
17
21
  export let onMountPreview = ({ canvas }) => {
22
+ const context = canvas.getContext('2d');
23
+
24
+ if (!context) {
25
+ throw new Error(`Cannot get CanvasRenderingContext2D from canvas`);
26
+ }
27
+
18
28
  return {
19
29
  canvas,
20
- context: canvas.getContext('2d'),
30
+ context,
21
31
  };
22
32
  };
23
33
 
24
34
  /**
25
- * @param {MountParams2DRenderer} params
26
- * @param {number} params.id
27
- * @param {HTMLCanvasElement} params.canvas
28
- * @param {HTMLElement} params.container
29
- * @param {number} params.width
30
- * @param {number} params.height
31
- * @param {number} params.pixelRatio
35
+ * @param {MountParams2DRenderer & PreviewParams} params
32
36
  */
33
- export let onResizePreview = ({ id, canvas, width, height, pixelRatio }) => {
37
+ export let onResizePreview = ({ canvas, width, height, pixelRatio }) => {
34
38
  canvas.width = width * pixelRatio;
35
39
  canvas.height = height * pixelRatio;
36
40
  canvas.style.width = `${width}px`;
@@ -1,5 +1,5 @@
1
1
  import { fragment } from '../lib/gl';
2
- import { client } from '@fragment/client';
2
+ import { client } from '../client';
3
3
  import { getShaderPath } from '../utils/glsl.utils';
4
4
  import { clearError } from '../state/errors.svelte';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import p5 from 'p5';
2
- import { client } from '@fragment/client';
2
+ import { client } from '../client';
3
3
  import { getShaderPath } from '../utils/glsl.utils';
4
4
  import { clearError } from '../state/errors.svelte';
5
5
 
@@ -29,8 +29,16 @@ let previews = [];
29
29
  * @param {number} params.pixelRatio
30
30
  * @returns {MountParamsP5GLRenderer}
31
31
  */
32
- export let onMountPreview = ({ id, container, canvas, width, height }) => {
32
+ export let onMountPreview = ({
33
+ id,
34
+ container,
35
+ canvas,
36
+ width,
37
+ height,
38
+ pixelRatio,
39
+ }) => {
33
40
  const p = new p5((sketch) => {
41
+ sketch.pixelDensity(pixelRatio);
34
42
  sketch.setup = () => {
35
43
  sketch.createCanvas(width, height, 'webgl', canvas);
36
44
  };
@@ -49,7 +57,7 @@ export let onMountPreview = ({ id, container, canvas, width, height }) => {
49
57
  };
50
58
 
51
59
  /**
52
- * @param {MountParamsP5GLRenderer} params
60
+ * @param {object} params
53
61
  * @param {number} params.id
54
62
  * @param {HTMLCanvasElement} params.canvas
55
63
  * @param {HTMLDivElement} params.container
@@ -65,7 +73,7 @@ export let onBeforeUpdatePreview = ({ id }) => {
65
73
  };
66
74
 
67
75
  /**
68
- * @param {MountParamsP5GLRenderer} params
76
+ * @param {object} params
69
77
  * @param {number} params.id
70
78
  * @param {HTMLCanvasElement} params.canvas
71
79
  * @param {HTMLDivElement} params.container
@@ -86,7 +94,7 @@ export let onAfterUpdatePreview = ({ id }) => {
86
94
  };
87
95
 
88
96
  /**
89
- * @param {MountParamsP5GLRenderer} params
97
+ * @param {object} params
90
98
  * @param {number} params.id
91
99
  * @param {HTMLCanvasElement} params.canvas
92
100
  * @param {number} params.width
@@ -103,7 +111,7 @@ export let onResizePreview = ({ id, width, height, pixelRatio }) => {
103
111
  };
104
112
 
105
113
  /**
106
- * @param {MountParamsP5GLRenderer} params
114
+ * @param {object} params
107
115
  * @param {number} params.id
108
116
  * @param {HTMLCanvasElement} params.canvas
109
117
  * @param {HTMLElement} params.container
@@ -25,8 +25,16 @@ let previews = [];
25
25
  * @param {number} params.pixelRatio
26
26
  * @returns {MountParamsP5Renderer}
27
27
  */
28
- export let onMountPreview = ({ id, container, canvas, width, height }) => {
28
+ export let onMountPreview = ({
29
+ id,
30
+ container,
31
+ canvas,
32
+ width,
33
+ height,
34
+ pixelRatio,
35
+ }) => {
29
36
  const p = new p5((sketch) => {
37
+ sketch.pixelDensity(pixelRatio);
30
38
  sketch.setup = () => {
31
39
  sketch.createCanvas(width, height, canvas);
32
40
  };