Screen helpers · interface

ExportTargetOptions

Explained in Recording and clips.

interface ExportTargetOptions
import type { ExportTargetOptions } from '@driftengine/core';

Properties

NameTypeDescription
sourcereadonlyHTMLCanvasElementThe canvas the scene is drawn into.
surfacereadonlyLockableSurfaceWhoever owns that canvas's drawing-buffer size.
widthreadonlynumber
heightreadonlynumber
fpsreadonlynumber
overlayreadonlyoptionalFrameOverlay | ((width: number, height: number) => FrameOverlay)Stamped into every frame, and baked once.
More

Accepts a function of the final frame size, because the size is not always the one asked for — a device that cannot hold the rate records smaller. A mark laid out for the larger frame and drawn into the smaller one lands on fractional pixels, which is the difference between a wordmark and a smudge.

drawFramereadonlyoptional(ctx: CanvasRenderingContext2D, width: number, height: number) => voidDrawn into every frame, live.
More

The counterpart to overlay, which is baked once because a wordmark and a share code do not change. Anything that does change per frame — a speedometer, a clock, a lap counter — cannot go through that path at all, and re-baking an overlay each frame would allocate a canvas per frame to say so. So this is handed the composite's own context instead and draws straight into it, after the scene and after the baked mark.

Called once per composed frame, including the still-image path, so a still carries whatever a clip would. Receives the final frame size for the same reason overlay does: a device that cannot hold the rate records smaller, and an instrument laid out for the larger frame lands on fractional pixels.

The context is owned by the target: a hook that leaves transforms, clips or composite state behind will corrupt later frames, so it must restore what it changes. Save/restore is wrapped here so the common case needs no discipline.

presentedFramesreadonlyoptional() => numberThe renderer's own presented-frame count, e.g. () => renderer.presentedFrames.
More

Optional, and reached for no reason but this: compose() composites once per encoded frame regardless of whether the renderer presented anything new since the call before — it has no way to ask, only to draw whatever is on source right now — so a scene that presents nothing between two frames of an export is composited twice, byte-identical, at two different timestamps. Passing this closes the loop: duplicateFrame after compose() says whether that just happened, read out of the renderer rather than guessed at from outside it. Omit it and compose() behaves exactly as before — nothing here forces or skips a draw, it only reports what already happened.