Studio tools
The editor combines ASCII, composition, saved looks and video export in one workspace. Start with your media, then choose a style. Open the editor to begin. Earlier workflows, including posting to the community, remain at Classic editor. Cell size controls ASCII detail; Dither uses Pixel size. Choose Adaptive preview for motion performance or Detail for fine cells. The editor reports the effective grid when a budget limits your selection. Export separates Image and Animation, displays valid pixel sizes, and saves the selected frame for PNG/JPG.
One configuration, from preview to export
From 4.9, Print & illustration (under Style) adds Stipple, Engraving, Crosshatch, Woodblock, Risograph and Pointillism. Adjust cell size, ink coverage and mark weight, then expand Print texture for angle, variation, seed and two-ink registration where applicable. Choose highlights for light ink or shadows for dark ink on paper. These styles work with every hover and lively-image motion, including in layered projects. Their settings carry into saved looks and exports.
From 4.5, choose Blue noise in Dither for a stable dispersed pattern. Under Style, in Tonal detail, adjust shadows, highlights, midtones and input black/white points before the source becomes characters or pixels. Reset tonal detail restores the neutral grade. These choices are saved with your look and used by both still and animated exports.
From 4.6, Channel curves (under Style) lets you edit RGB, red, green and blue independently. Choose a point, adjust its input/output, or add another point; reset one channel or all curves. Reduce noise in Tonal detail filters the source before conversion, with edge preservation. Both are off or neutral by default and carry into saved looks and exports.
Source shaping (under Style) combines Twirl, Pinch, Spherize, Ripple, Zigzag, Shear, Smudge, Polar and Fragment. Add up to eight warps; they apply from top to bottom before conversion. Open a row to adjust it. Drag its handle, use the handle’s arrow keys, or use Move up and Move down. The eye button bypasses one warp without losing its settings. You can also duplicate, reset or remove it. Each structural change is separately undoable.
Size follows the canvas’s shorter side; negative strength reverses direction. Position contains the horizontal and vertical center. Choose Reveal the backdrop when warped pixels leave the image, and a transparent backdrop for transparent PNG edges. Lively images and hover remain independent. Single-image and layered editors share these controls; saved looks and exports retain the same order and settings.
Version 4.2 adds Hex mosaic, LED matrix and CMYK print. In Dither, edit any palette swatch to create a custom palette, choose RGB or luminance mapping, and adjust pattern speed and direction. The Detail option supports 1px dither in the 960px reference frame; dense animation may run slower. Effects includes motion strength, RGB split and sharpening. Saved settings and exports use the same choices.
import { mountStudio } from 'asciify-engine/studio';
const studio = await mountStudio(canvas, '/your-video.mp4', {
settings: {
style: 'ascii',
ink: '#e8b900',
backdrop: { mode: 'blurred', opacity: 0.4 },
},
maxDimension: 960,
onError: error => console.error(error),
});
studio.update({ hover: { effect: 'water', radius: 0.35 } });
// On unmount:
studio.destroy();Use the optional asciify-engine/studio entry from version 1.4.0. Version 2.0 removes Emoji, Musical and Starfield preset names; custom character strings remain supported. Existing core and hover entry points stay available. Font data and video encoders load when used. StudioInput accepts partial settings; updates merge nested groups and replace arrays.
Choose your tools
| Option | Type | Default | Description |
|---|---|---|---|
Style | style / dither | ASCII | Character shapes, dots, pixel, mosaic, studded cells, isometric and faceted tiles. Ordered or error-diffusion dithering with curated or custom palettes. |
Print texture | Coverage 1, weight 1 | Stipple, engraving, crosshatch, woodblock, risograph and pointillism. Repeatable texture with adjustable polarity, weight and coverage; angle and ink registration apply to the corresponding techniques. | |
Composition | aspectRatio / crop / mask | Original | Ratio, source zoom and position, quarter-turn rotation, rectangle/ellipse/freehand masks. Coordinates are normalized to the canvas. |
Source shaping | warps / warpEdge | Empty stack | Up to eight ordered Twirl, Pinch, Spherize, Ripple, Zigzag, Shear, Smudge, Polar or Fragment transforms. Shared by all styles; independent from hover and still-image motion. |
Backdrop | backdrop | Solid charcoal | Solid, transparent, source image, softened source, or a two-color gradient. Wallpaper is an optional visual composition. |
Color and light | color / lights | No grade | Brightness, contrast, saturation, grayscale, levels, gamma, shadows/highlights, channel curves, source noise reduction, tint/blend and up to four positionable radial lights. |
Finish | effects | Off | Character glow, scene bloom, Prism, grain, dust, scanlines, CRT, vignette, glitch, pixelation, halftone and four blur modes. Stack effects deliberately. |
Looks | StudioSettings | Version 1 | Apply a curated look, save on this device, restyle, share a settings link, or import/export JSON. Media files are not included. |
Text | createStudioText | Standard | Load one FIGlet font at a time, copy plain ASCII, or use the rendered text as artwork for animation and export. |
Export files without recording the preview
import { loadStudioMedia, exportStudio } from 'asciify-engine/studio';
const abort = new AbortController();
const media = await loadStudioMedia(file, abort.signal);
try {
const blob = await exportStudio({
frame: async (time, signal) => {
await media.seek(time, signal);
return media.frame(time);
},
}, settings, {
format: 'mp4', width: 1280, height: 720,
referenceWidth: 640, // preserve a 640px preview's cell density at 2×
duration: 8, fps: 24, signal: abort.signal,
onProgress: value => console.log(value),
});
// Download the Blob or pass it to your application's file workflow.
} finally {
media.destroy();
}PNG and JPG snapshots, GIF, MP4/H.264 and WebM/VP9 use the same render pipeline. Video rendering advances explicit frames and waits for the encoder; preview stutters are not recorded. MP4/WebM require compatible browser WebCodecs support. Use even dimensions for MP4. Videos are silent; live pointer trails are not included in offline exports. For a selected PNG/JPG frame, pass time: studio.time and motionTime: studio.motionTime before starting asynchronous export; this preserves the displayed phase after animation-speed edits.
| Option | Type | Default | Description |
|---|---|---|---|
Still image | png / jpeg | 4096px per side | Maximum 16 megapixels. PNG retains transparency; JPG flattens transparent areas. |
MP4 / WebM | mp4 / webm | Up to 60 fps | Maximum 3840×2160 pixels in total and 60 seconds. A supported encoder is required; an error explains unsupported formats. |
GIF | gif | Up to 600 frames | Maximum 1280×720 pixels. GIF timing is rounded across frames to hundredths of a second. MP4 is usually smaller. |
Cancellation | AbortSignal | Optional | Abort loading or export. The live preview owns a separate media instance and stays intact. |
Grid and density
studioGrid reports the grid a bounded preview or export will actually draw, and the smallest cell its budget allows. Dither reads dither.scale; every other style reads cellSize. Pass a pixel ratio to keep a preview's density in a larger export.
import { studioGrid } from 'asciify-engine/studio';
// (width, height, settings, maxCells = 12000, pixelRatio = 1)
const grid = studioGrid(960, 540, studio.settings);
// { cell, rowHeight, columns, rows, minimum, limited }
if (grid.limited) note(`Cells start at ${grid.minimum}px at this size`);
// The same density in a 2× export:
const exported = studioGrid(1920, 1080, studio.settings, 12000, 2);Defaults stay performance-bounded. Opt into a larger budget with the mount's maxCells option for one-pixel output, and use setAdaptive(false) for a fixed-budget measurement.
Tone, curves and noise
Grade the source before it becomes characters or pixels. Tonal detail is a bounded monotone curve: gamma, shadows, highlights, blackPoint and whitePoint. Channel curves edit RGB, red, green and blue with up to 16 points each, in 0–1 coordinates. denoise (0–1) is edge-preserving source noise reduction; on dense video it is a CPU cost. All defaults are neutral and every style shares them.
studio.update({
color: {
gamma: 1.15, shadows: 0.2, highlights: -0.1,
denoise: 0.25,
curves: { rgb: [[0, 0], [0.25, 0.2], [0.75, 0.8], [1, 1]] },
},
dither: { algorithm: 'blue-noise' }, // stable dispersed pattern; 'noise' stays white noise
});
const saved = studio.settings; // detached snapshot for save and exportBlue noise is precomputed: no runtime generation and no asset download. Build an algorithm picker from DITHER_ALGORITHMS; drift and shimmer stay separate under dither.motion.
Source shaping
Up to MAX_STUDIO_WARPS (eight) transforms apply in list order before any style. Generate controls from STUDIO_WARPS: each entry has value, label, description and whether it takes direction (angle) and frequency.
import { STUDIO_WARPS, MAX_STUDIO_WARPS } from 'asciify-engine/studio';
// twirl | pinch | sphere | ripple | zigzag | shear | smudge | polar | fragment
studio.update({
warps: [
{ type: 'sphere', amount: 0.3, radius: 0.7 },
{ type: 'shear', amount: 0.15, angle: 25 },
{ type: 'ripple', amount: 0.2, frequency: 6, enabled: false }, // bypassed, settings kept
],
warpEdge: 'clamp', // or 'transparent'
});Radius is a fraction of the canvas's shorter side; x and y place the center. The stack is static shaping: motion and hover stay independent, and an empty stack costs nothing.
Keep it responsive
Still images sleep when motion and hover settle. Video sampling follows decoded source frames while pointer animation can run at display cadence. The preview has a bounded working size and can reduce its cell budget under sustained load; saved settings are retained. Use adaptive: false for a fixed-budget measurement. Test on your target devices before making a frame-rate claim.
Optical finishing uses WebGL. Check capabilities.gpuFinish after rendering an effect, and keep the base artwork available when GPU finishing is unavailable. For direct rendering of changing canvas sources, call invalidate before render. Always destroy mounted renderers on navigation.
Character size and preview sharpness
ASCII starts with a finer 6-unit cell size. Smaller cells reveal more detail. The preview uses a separate backing resolution for sharp text on high-density displays, while preserving the same character grid for export. Switching to a tile style no longer makes ASCII inherit its larger cell size.
Bring a still image to life
Lively images (under Effects) offers 10 motions for photos and still artwork. Each one reads the photo’s own light and depth rather than laying a pattern over it: Relight moves a key light around the subject’s relief, Specular sweep glances across the highlights, Parallax and Slow push move near and far apart, Focus pull racks between subject and background, Mist and Drift move only the distance and smooth grounds, and Develop brings the print back in from its highlights. Choose a starting combination, then tune motion speed, strength and hover independently. Off remains the default. A motion cycle lasts 12 / speed seconds; videos and animated finish effects may have different loop boundaries.
instance.update({ motion: { type: 'current', speed: 1 } });
// 'none' | 'relight' | 'sheen' | 'glow' | 'shimmer' | 'parallax' | 'push' | 'focus' | 'mist' | 'current' | 'develop'
// Each cycle lasts 12 / speed seconds. Pause and reduced motion show the still photo.
// After 4.21: caustics, grain, trace, tidal, weave, breeze, reform, print and unfold load as their closest replacement.DEPRECATED_STUDIO_MOTIONS (from /studio or /hover) maps each retired ID to its replacement, so saved settings can be migrated explicitly.
import { DEPRECATED_STUDIO_MOTIONS } from 'asciify-engine/studio';
// { caustics: 'sheen', grain: 'shimmer', trace: 'relight', tidal: 'current', weave: 'current',
// breeze: 'current', reform: 'develop', print: 'develop', unfold: 'develop' }
const type = DEPRECATED_STUDIO_MOTIONS[saved.motion.type as keyof typeof DEPRECATED_STUDIO_MOTIONS] ?? saved.motion.type;Compose multiple layers
From 4.8, combine up to eight images, videos or text layers in the layered studio. Each layer has its own style, hover, still-image motion, placement, opacity and blend mode. Undo, source replacement and project JSON keep it editable. Text is saved in the editor’s project; image/video files must be reattached when reopening on another device.
In an app, use mountStudioProject with one source dictionary and one shared budget. Update a layer with updateLayer; export using exportStudioProject so live playback is untouched. Pointer picking follows transformed rectangular layer bounds. A single budget limits total work, and an explicit Detail option allows denser output at a higher cost.
import { mountStudioProject, serializeStudioProject, parseStudioProject } from 'asciify-engine/studio';
const player = await mountStudioProject(canvas, { layers: [
{ id: 'photo', source: 'image', settings: { motion: { type: 'parallax' } } },
{ id: 'title', source: 'text', placement: { width: 0.8, height: 0.25 },
settings: { ink: '#b3ed82', hover: { effect: 'elastic' } } },
] }, { image: '/your-photo.webp', text: { text: 'HELLO', font: 'Small' } });
player.updateLayer('photo', { settings: { hover: { effect: 'water' } } });
// Save the layout. Image and video files are not included: keep them and rebind by source key.
const json = serializeStudioProject(player.project);
// Restore: validates kind, version and layer ids, and throws on anything else (1 MB limit).
const project = parseStudioProject(json);
// player.destroy() on unmountRead the layered project API guide or single-source Studio guide. Accessible hero headlines and controls should remain in HTML; see the text layering guide.