import { Context, ContextType } from '@daeinc/canvas'; import { Format } from 'gifenc'; type Sketch = (props: FinalProps) => Promise | void; type SketchRender = (props: FinalProps) => Promise | void; type SketchResize = (props: FinalProps) => void; type SketchLoop = (timestamp: number) => void; type SketchMode = Context; type FrameFormat = "png" | "jpg" | "jpeg" | "webp"; type FramesFormatObj = Format extends "mp4-browser" ? { /** * Render mp4 video in browser using WebCodecs API. Browser support varies. */ format: Format; /** * Specify which codec to use. * See [`fullCodecString`](https://mediabunny.dev/guide/media-sources#video-encoding-config) from Mediabunny for more info. * * @default * ["avc", "avc1.4d002a"] */ codecStrings: ["avc" | "hevc" | "vp8" | "vp9" | "av1", string]; } : { format: Format; /** * Specity which codec to use. * See [`fullCodecString`](https://mediabunny.dev/guide/media-sources#video-encoding-config) from Mediabunny for more info. * * @default * ["vp9", "vp09.00.10.08"] */ codecStrings: ["vp8" | "vp9" | "av1", string]; }; type FramesFormatStr = "gif" | "mp4" | "mp4-browser" | "webm" | "png"; type FramesFormat = FramesFormatStr | FramesFormatObj<"mp4-browser" | "webm">; /** GIF encoding options */ type GifOptions = { /** * Max number of colors to use for quantizing each frame * @default 256 */ maxColors?: number; /** * `"rgb565"` (default), `"rgb444"`, or `"rgba4444"` * @default "rgb565" */ format?: Format; /** use a palette instead of quantizing */ palette?: number[][]; }; type Hotkeys = { togglePlay?: boolean; exportFrame?: boolean; exportFrames?: boolean; git?: boolean; }; /** * User provided settings. Any properties not defined by user will be merged internally with default settings. */ type SketchSettings = { /** Set HTML webpage title. it replaces the `` tag and is displayed on top of browser window */ title?: string; /** * Set background color of HTML page. uses CSS color string. ex. `"#aaa"` * @default "#333" */ /** * Set sketch mode to use for either 2d or 3d sketches. * @default "2d" */ mode?: SketchMode; /** * Set the HTML5 Canvas element's id attribute. * @default "ssam-canvas" */ id?: string; /** * Set canvas parent either as `HTMLElement` object or string selector. ex. `div#app` * If Ssam uses an existing canvas element, this setting is ignored and Ssam will use the existing DOM tree. * @default "body" */ parent?: HTMLElement | string; /** Set it to use an existing canvas instead of using one provided by Ssam. */ canvas?: HTMLCanvasElement; /** Set the dimensions of canvas: `[width, height]`. Set it to `null` or `undefined` to use fullscreen canvas. */ dimensions?: [number, number] | null; /** Set pixel ratio */ pixelRatio?: number; /** * Apply inline CSS transform to scale canvas to its parent. * @default true */ scaleToParent?: boolean; /** * Scale context to account for pixelRatio * @default true */ scaleContext?: boolean; /** * When `true`, it sets the following options: * ```javascript * canvas.style.imageRendering = "pixelated"; * ctx.imageSmoothingEnabled = false; * ``` * @default false */ pixelated?: boolean; /** You can add context attributes for 2d or webgl canvas */ attributes?: CanvasRenderingContext2DSettings | WebGLContextAttributes; /** * Set to `false` for static sketches * @default true */ animate?: boolean; /** Set plackback frame rate */ playFps?: number; /** * Set export frame rate for videos. * @default 60 */ exportFps?: number; /** Set animation loop duration in milliseconds */ duration?: number; /** * How many times to loop (repeat). All time-related props except `loopCount` are reset each loop. * @default 1 */ numLoops?: number; /** Set export file name. By default, Ssam uses datetime string */ filename?: string; /** Set prefix to file name */ prefix?: string; /** Set suffix to file name */ suffix?: string; /** Set file format for image export (ie. `png`, `jpg`). you can also use an array to export multiple formats at the same time. ex. `["webp", "png"]` */ frameFormat?: FrameFormat | FrameFormat[]; /** * Set file format for video/sequence export (ie. `webm`, `gif`, `mp4-browser`). you can also use an array to export multiple formats at the same time. ex. `["gif", "webm"]` * * @example * ```ts * framesFormat: "mp4" * ``` * * @example * ```ts * framesFormat: { * format: "mp4-browser", * codecStrings: ["avc", "avc1.4d002a"] * } * ``` */ framesFormat?: FramesFormat | FramesFormat[]; /** GIF export options. */ gifOptions?: GifOptions; /** * Set it to either `true` or `false` to enable or disable all Ssam-provided hotkeys (ex. `CMD+S` for image export). * Or, disable individual hotkeys selectively by setting `togglePlay`, `exportFrame`, `exportFrames`, `git`. * * @default * true * * @example * hotkeys: true // use all Ssam-provided hot keys (export frame, git commit, etc.) * hotkeys: false // disable all Ssam-provided hot keys * hotkeys: { * togglePlay: false, // disable spacebar hotkey. all other hotkeys still work. * } * */ hotkeys?: boolean | Hotkeys; /** Send extra data to the sketch. it is accessible via `props.data` */ data?: Record<string, any>; }; /** * Settings that are used internally for development and not exposed to users. ie. exportTotalFrames */ interface SketchSettingsInternal { title: string; mode: SketchMode; id: string; /** The default parent is `body` */ parent: HTMLElement | string; /** If `null`, a new canvas is created */ canvas: HTMLCanvasElement | null; dimensions: [number, number] | null; pixelRatio: number; scaleToParent: boolean; scaleContext: boolean; pixelated: boolean; attributes?: CanvasRenderingContext2DSettings | WebGLContextAttributes; animate: boolean; /** If null, will use display's maximum fps */ playFps: number | null; exportFps: number; duration: number; totalFrames: number; exportTotalFrames: number; numLoops: number; filename: string; prefix: string; suffix: string; frameFormat: FrameFormat[]; framesFormat: FramesFormat[]; gifOptions: GifOptions; hotkeys: Hotkeys; data: Record<string, any>; } interface SketchStates { /** Regardless, time keeps updating */ paused: boolean; playMode: "play" | "record"; savingFrame: boolean; /** REVIEW: I don't think this is being used anymore? */ startTime: number; lastStartTime: number; pausedStartTime: number; pausedDuration: number; timestamp: number; lastTimestamp: number; frameInterval: number | null; deltaRemainder: number; timeResetted: boolean; firstLoopRender: boolean; firstLoopRenderTime: number; /** not being used atm. */ timeNavOffset: number; recordedFrames: number; /** First frame doesn't have `prevFrame`, so it's set to `null` */ prevFrame: number | null; } /** props that are shared by all sketch modes */ type BaseProps<Mode extends SketchMode> = { wrap: Wrap<Mode>; /** `HTMLCanvasElement` */ canvas: HTMLCanvasElement; /** Canvas width. may be different from `canvas.width` due to `pixelRatio` scaling */ width: number; /** Canvas height. may be different from `canvas.height` due to `pixelRatio` scaling */ height: number; /** Try `window.devicePixelRatio` to get the high resolution if your display supports */ pixelRatio: number; /** * When `settings.duration` is set, `playhead` will repeat 0..1 over duration. If no duration, it will always be `0`. */ playhead: number; /** * Frame count. starts at `0`. To keep up with time, some frames may skip and it may not increment by `1` all the time. */ frame: number; /** * Elapsed time in milliseconds. when it reaches `duration`, it will reset to `0` */ time: number; /** Time it took between renders in milliseconds */ deltaTime: number; /** Animation duration in milliseconds. when it reaches the end, it will loop back to the beginning */ duration: number; /** Number of total frames over duration */ totalFrames: number; /** The current loop count. it is based on `settings.numLoops`. it increases by `1` and resets back to `0`. `settings.duration` is required. */ loopCount: number; /** * The number of loops to repeat in the sketch that is defined in `settings.numLoops` * It may have been updated by `update()` function prop. */ numLoops: number; /** Playback frame rate. Ssam throttles the rendering frequency but the exact fps is not guaranteed. */ playFps: number | null; /** * Export frame rate. You can use very high frame rate, but GIF format is capped at 50fps. */ exportFps: number; /** `true` if recording is in progress */ recording: boolean; /** Call to export canvas as image in the format(s) specified in `settings.frameFormat`*/ exportFrame: () => void; /** * Call to export canvas as frames or video in the format(s) specified in `settings.framesFormat`. * Calling it again while recording will end the current recording. */ exportFrames: () => void; /** Call to play or pause sketch */ togglePlay: () => void; /** Call without any props for rendering-on-demand. it will call sketch's returned function. good for manually advancing animation frame-by-frame. */ render: () => void; resize: () => void; /** * Some sketch settings can be updated within sketch by calling `update()` with key/value pair. * @example * update({ duration: 4_000 }) */ update: (options: Record<string, any>) => void; /** Extra data sent from `settings.data`. */ data: Record<string, any>; }; type SketchContext = ContextType<SketchMode>; /** * to use with canvas with 2d sketches */ interface SketchProps extends BaseProps<"2d"> { context: CanvasRenderingContext2D; } /** * props type specific to `webgl` or `webgl2` mode */ interface WebGLProps extends BaseProps<"webgl"> { /** webgl context */ gl: WebGLRenderingContext; } interface WebGL2Props extends BaseProps<"webgl2"> { /** webgl context */ gl: WebGL2RenderingContext; } interface WebGPUProps extends BaseProps<"webgpu"> { /** webgpu context */ context: GPUCanvasContext; } type VideoEncodeParams<Mode extends SketchMode> = { canvas: HTMLCanvasElement; settings: SketchSettingsInternal; states: SketchStates; props: FinalProps<Mode>; }; type FinalProps<Mode extends SketchMode> = Mode extends "2d" ? SketchProps : Mode extends "webgl" ? WebGLProps : Mode extends "webgl2" ? WebGL2Props : Mode extends "webgpu" ? WebGPUProps : never; declare const ssam: <Mode extends SketchMode>(sketch: Sketch<Mode>, settings: SketchSettings) => Promise<Wrap<Mode> | undefined>; declare class Wrap<Mode extends SketchMode> { userSettings: SketchSettings; settings: SketchSettingsInternal; states: SketchStates; props: FinalProps<Mode>; removeResize: () => void; removeKeydown: () => void; unload?: (props: FinalProps<Mode>) => void; private raf; globalState: Record<string, any>; count: number; loop: (timestamp: number) => void; /** Runs right before export. Useful if some values need to reset for exporting. */ preExport?: () => void; /** Runs right after export. */ postExport?: () => void; setupGifAnimRecord: () => Promise<void>; encodeGifAnim: ({ context, settings, states, props, }: { context: SketchContext; settings: SketchSettingsInternal; states: SketchStates; props: FinalProps<Mode>; }) => void; endGifAnimRecord: ({ settings, }: { settings: SketchSettingsInternal; }) => void; setupWebMRecord: ({ settings, props, }: { settings: SketchSettingsInternal; props: FinalProps<Mode>; }) => void; encodeWebM: (params: VideoEncodeParams<Mode>) => Promise<void>; endWebMRecord: ({ settings, }: { settings: SketchSettingsInternal; }) => Promise<void>; setupMp4BrowserRecord: ({ settings, states, props, }: { settings: SketchSettingsInternal; states: SketchStates; props: FinalProps<Mode>; }) => void; encodeMp4Browser: (params: VideoEncodeParams<Mode>) => void; endMp4BrowserRecord: ({ settings, }: { settings: SketchSettingsInternal; }) => Promise<void>; gitCb: (data: any) => void; ssamGitSuccessCallback(data: any): void; constructor(); setup(sketch: Sketch<Mode>, userSettings: SketchSettings): Promise<null | undefined>; /** * Calls `this.unloadCombined()` and remove canvas */ hotReload(): void; /** * Cancel animation frame, remove listeners (resize, keydown) and calls `this.unload()` */ unloadCombined(): void; /** * Turn off socket listeners (for plugins) */ dispose(): void; run(): void; playLoop(timestamp: number): Promise<null | undefined>; recordLoop(): Promise<null | undefined>; resetAfterRecord(): void; /** * `wrap.render(props)` handles animation loop. * Place any code you want updated each frame. */ render(props: FinalProps<Mode>): Promise<void> | void; /** * `wrap.resize(props)` is called when the canvas is resized. * You can get the updated dimensions with `props.width` and `props.height`. */ resize(props: FinalProps<Mode>): void; handleResize(): void; preExportCombined(): void; postExportCombined(): void; noLoop(): void; } export { type FinalProps, type FrameFormat, type FramesFormat, type GifOptions, type Sketch, type SketchLoop, type SketchMode, type SketchProps, type SketchRender, type SketchResize, type SketchSettings, type WebGL2Props, type WebGLProps, type WebGPUProps, Wrap, ssam };