Image API — `ImageHandle` · `Engine`
Diese Seite ist derzeit nur englisch — die JSDoc-Kommentare in der Engine sind englisch verfasst. Eine deutsche Auto-Generierung folgt, sobald die Methoden mit
@apidoc-de-Tags ausgestattet sind. — Zur Übersicht
All pixel-processing operations live on ImageHandle (the value you get back from Engine.loadImage() / Engine.createImage()). The Engine global is the entry point for loading, creating, and saving images, plus the canvas / path / animation / tool / timer factories.
Classes
ImageHandle
A handle to an image stored in the Rust image registry.
Most methods return this for fluent chaining:
const img = Engine.loadImage("input.png");
img.brightness(1.2).gaussianBlur(1.5).save("out.png");
Size-changing operations (crop, resize, pad, rotateExpand, warpPolar,
warpPerspective) update width and height automatically.
Accessors
height
Get Signature
get height(): number;
Current image height in pixels. Updated automatically by size-changing ops.
Returns
number
Set Signature
set height(v): void;
Parameters
| Parameter | Type |
|---|---|
v |
number |
Returns
void
id
Get Signature
get id(): number;
Internal handle id (read-only). Needed for cross-image ops like blend/applyMask.
Returns
number
width
Get Signature
get width(): number;
Current image width in pixels. Updated automatically by size-changing ops.
Returns
number
Set Signature
set width(v): void;
Parameters
| Parameter | Type |
|---|---|
v |
number |
Returns
void
Methods
_setHeightAndWidthUnsafe()
_setHeightAndWidthUnsafe(h, w): void;
Parameters
| Parameter | Type |
|---|---|
h |
number |
w |
number |
Returns
void
addNoise()
addNoise(opts?, region?): this;
Add synthetic sensor noise — the core degradation primitive for building model-training data (Real-ESRGAN / GFPGAN style: take a clean image, degrade it, train the network to invert it).
Unlike filmGrain (uniform mono grain only), this exposes the distribution shapes a degradation pipeline actually needs:
"gaussian"— normal noise, constant standard deviationsigma. The classic additive read-noise."uniform"— flat noise in±sigma(same asfilmGrain)."poisson"— shot noise whose strength scales with√pixel: bright regions get noisier, shadows stay clean. Modelled as the standard Gaussian approximation of Poisson statistics;sigmais the overall gain.
Parameters
| Parameter | Type | Description |
|---|---|---|
opts? |
{ color?: boolean; sigma?: number; type?: "gaussian" | "uniform" | "poisson"; } |
- |
opts.color? |
boolean |
false (default) shares one sample across RGB (luminance grain); true perturbs each channel independently (chroma noise). Alpha is never touched. |
opts.sigma? |
number |
Noise strength (≈ [0..1]). Default 0.05. |
opts.type? |
"gaussian" | "uniform" | "poisson" |
Distribution. Default "gaussian". |
region? |
Rect | RegionOpts |
Optional region to limit the effect to. |
Returns
this
this
Note
Non-deterministic — result varies between runs.
Example
// Real-ESRGAN-style degradation step
img.gaussianBlur(1.5)
.resize(img.width / 2, img.height / 2)
.addNoise({ type: "poisson", sigma: 0.08, color: true });
adjustAlpha()
adjustAlpha(
value,
mode?,
region?): this;
Adjust the alpha (transparency) channel.
Parameters
| Parameter | Type | Description |
|---|---|---|
value |
number |
Target value; meaning depends on mode. |
mode |
AlphaMode |
AlphaMode.Set = set directly; Mul = multiply; Add = add (clamped). Default: Set. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
anisotropicBlur()
anisotropicBlur(sigmaX, sigmaY): this;
Anisotropic Gaussian blur — independent sigmas on the X and Y
axes. Useful for lens-style streak effects, fake shallow depth
of field, or just a "stretched lights" look. When sigmaX === sigmaY this matches gaussianBlur(sigmaX). Either sigma
≤ 0 disables that axis (so a horizontal-only or
vertical-only smear is just anisotropicBlur(8, 0) /
anisotropicBlur(0, 8)).
Parameters
| Parameter | Type | Description |
|---|---|---|
sigmaX |
number |
Horizontal Gaussian sigma in pixels. |
sigmaY |
number |
Vertical Gaussian sigma in pixels. |
Returns
this
this
Example
// Horizontal motion-streak hint — vertical detail intact.
img.anisotropicBlur(12, 1);
// Camera-shake-style stretch in both axes, more horizontal.
img.anisotropicBlur(8, 3);
anisotropicKuwahara()
anisotropicKuwahara(radius, region?): this;
Anisotropic Kuwahara filter (Kyprianidis 2009) — the
state-of-the-art painterly stylisation. Where kuwahara uses
four axis-aligned quadrants, this version drives the kernel
geometry from the local image structure: brush strokes
follow the dominant edge direction (hair runs along hair,
ridges along ridges) and stretch into long, thin shapes in
highly-directional regions. Result: noticeably closer to a
real painting than the classical version, especially around
faces, fabric and natural texture.
Cost is ~3-5× the classical Kuwahara at the same radius —
we additionally compute a structure tensor (smoothed
gradient outer product) before the per-pixel sector
accumulation. radius 6-12 is typical; larger looks more
"broad-stroke".
Parameters
| Parameter | Type | Description |
|---|---|---|
radius |
number |
Maximum kernel half-extent in pixels (≥ 1). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
Example
img.anisotropicKuwahara(8); // painterly portrait
img.bilateralBlur(5, 0.25, 8)
.anisotropicKuwahara(10)
.saturation(1.10); // pre-smooth for cleaner strokes
applyMask()
applyMask(mask): this;
Apply a luminance mask — the mask image's greyscale value sets this image's alpha.
Parameters
| Parameter | Type | Description |
|---|---|---|
mask |
ImageHandle |
Mask image (must have the same dimensions). |
Returns
this
this
artisticBold()
artisticBold(size?): this;
Ink-drawing effect — black-on-white outline silhouette built
from canny(0.1, 0.2) → dilate(size) → invert. Kept as a
convenience alias.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
size |
number |
3 |
Dilation radius. Larger = thicker ink lines. Default 3. |
Returns
this
this
Deprecated
Convenience alias. New code should compose the
three primitives explicitly so the intent is
visible:
img.canny(0.1, 0.2).dilate(size).invert().
Or for a softer / shaded sketch look, use
img.pencilSketch(strength).
autocrop()
autocrop(opts?): this;
Trim a uniform-colour or transparent border. Finds the smallest
rectangle containing every non-background pixel and crops to
it. Updates width and height. When the whole image is
background, returns this unchanged.
Default behaviour (no opts): trim transparent margins
(α ≤ 0.001).
Parameters
| Parameter | Type | Description |
|---|---|---|
opts? |
{ color?: Color; padding?: number; tolerance?: number; } |
- |
opts.color? |
Color |
If set, trim that solid colour instead of transparency (Pixel or hex string). α=0 pixels are also treated as background so mixed transparent + flat-colour borders work without a second pass. |
opts.padding? |
number |
Pixels of margin to keep around the detected box. Default 0. Clamped to image bounds. |
opts.tolerance? |
number |
How close to the background a pixel can be before it counts as content. In [0, 1]. Default 0.001 (alpha) / 0.05 (color). |
Returns
this
this
Example
// Drop transparent edges
Engine.loadImage("logo.png").autocrop().save("logo_tight.png");
// Drop a white frame around a scan
Engine.loadImage("scan.jpg")
.autocrop({ color: "#ffffff", tolerance: 0.04, padding: 8 })
.save("trimmed.jpg");
bilateralBlur()
bilateralBlur(
d,
sigmaColor,
sigmaSpace,
region?): this;
Bilateral blur — edge-preserving smoothing.
Parameters
| Parameter | Type | Description |
|---|---|---|
d |
number |
Diameter of pixel neighbourhood. |
sigmaColor |
number |
Color sigma (larger = smoother colors). |
sigmaSpace |
number |
Space sigma (larger = wider area). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
blend()
blend(
other,
factor,
mode?): this;
Blend another image onto this one. Both images must have the same dimensions.
Parameters
| Parameter | Type | Description |
|---|---|---|
other |
ImageHandle |
The image to blend in. |
factor |
number |
Blend weight ∈ [0..1]: 0 = only self, 1 = only other. |
mode? |
BlendMode |
Blend mode (default: Normal = 0). |
Returns
this
this
blendAt()
blendAt(
other,
at,
factor,
mode?): this;
Blend another image onto this one at a specific pixel position.
The other image may be smaller — it is composited at at.
Parameters
| Parameter | Type | Description |
|---|---|---|
other |
ImageHandle |
The image to composite. |
at |
Px |
Pixel-space offset, e.g. px(100, 50) or px(0, 0). |
factor |
number |
Blend weight ∈ [0..1]. |
mode? |
BlendMode |
Blend mode (default: Normal = 0). |
Returns
this
this
boxBlur()
boxBlur(size, region?): this;
Box blur (average filter).
Parameters
| Parameter | Type | Description |
|---|---|---|
size |
number |
Kernel half-size in pixels. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
brightness()
brightness(factor, region?): this;
Adjust brightness.
Parameters
| Parameter | Type | Description |
|---|---|---|
factor |
number |
Multiplier: 1.0 = unchanged, > 1.0 = brighter, < 1.0 = darker. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
canny()
canny(
low,
high,
region?): this;
Canny edge detection (two-threshold).
Parameters
| Parameter | Type | Description |
|---|---|---|
low |
number |
Low hysteresis threshold ∈ [0..1]. |
high |
number |
High hysteresis threshold ∈ [0..1]. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
clone()
clone(): ImageHandle;
Create a deep copy of this handle. The clone is independent — operations on it do not affect the original.
Returns
A new ImageHandle with its own pixel data.
Throws
If the clone fails.
colorJitter()
colorJitter(
brightness,
contrast,
saturation,
hue,
region?): this;
Randomized color jitter for data augmentation. Each parameter applies random variation in the given range.
Parameters
| Parameter | Type | Description |
|---|---|---|
brightness |
number |
Brightness jitter magnitude. |
contrast |
number |
Contrast jitter magnitude. |
saturation |
number |
Saturation jitter magnitude. |
hue |
number |
Hue jitter magnitude (degrees). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
Note
Non-deterministic — result varies between runs.
colorMatch()
colorMatch(reference): this;
Reinhard per-channel mean/std colour transfer — shifts this
image's tone to match reference. Per channel:
new = (old - mean_self) * std_ref / std_self + mean_ref
Alpha-weighted, so transparent borders (e.g. from
warpPerspective outside-source areas) don't skew the source
mean toward zero. Useful for blending AI-restored faces
(GFPGAN, CodeFormer) back into a photo's skin-tone
neighbourhood: pass the original aligned face crop before
warping the restored version back. Dimensions must match.
Parameters
| Parameter | Type |
|---|---|
reference |
ImageHandle |
Returns
this
Example
const aligned = orig.clone().warpPerspective(512, 512, fwd);
const restored = Engine.tool('gfpgan-restore').apply(aligned);
restored.colorMatch(aligned); // match orig face's tone
aligned.free();
colorTransfer()
colorTransfer(reference): this;
Reinhard color transfer — match this image's per-channel mean and standard deviation in log-LMS space to a reference image's. Useful for harmonising photos shot under different lighting, matching screenshot tones across OS themes, or warming/cooling a target to fit a hero shot.
Pixels with α = 0 are skipped (left untouched).
If you transfer the same reference onto many targets, prefer
Engine.colorStats(ref) once → target.colorTransfer(stats)
per target, so the reference stats aren't recomputed every time.
Parameters
| Parameter | Type | Description |
|---|---|---|
reference |
LmsStats | ImageHandle |
Either an ImageHandle (stats computed on the spot) or a precomputed LmsStats object from Engine.colorStats(). |
Returns
this
this
Example
const warm = Engine.loadImage("hero_warm.jpg");
Engine.loadImage("cold_screenshot.png")
.colorTransfer(warm)
.save("harmonised.png");
contrast()
contrast(factor, region?): this;
Adjust contrast.
Parameters
| Parameter | Type | Description |
|---|---|---|
factor |
number |
1.0 = unchanged, > 1.0 = higher contrast, 0.0 = flat grey. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
convolve()
convolve(
kernel,
kw?,
kh?): this;
Apply a custom 2-D convolution kernel.
The kernel is a flat row-major array applied to the RGB channels. Alpha is preserved. Output is clamped to [0..1].
If kw and kh are omitted the kernel is assumed to be square
(sqrt(kernel.length) × sqrt(kernel.length)).
Parameters
| Parameter | Type | Description |
|---|---|---|
kernel |
number[] |
Flat row-major kernel values. |
kw? |
number |
Kernel width (default: Math.sqrt(kernel.length)). |
kh? |
number |
Kernel height (default: same as kw). |
Returns
this
this
Examples
img.convolve([0, -1, 0, -1, 5, -1, 0, -1, 0]);
img.emboss();
img.convolve(new Array(9).fill(1/9), 9, 1);
crop()
crop(
x,
y,
w,
h): this;
Crop a rectangular region.
Updates width and height.
Parameters
| Parameter | Type | Description |
|---|---|---|
x |
number |
Left edge in pixels. |
y |
number |
Top edge in pixels. |
w |
number |
Crop width in pixels. |
h |
number |
Crop height in pixels. |
Returns
this
this
differenceMap()
differenceMap(reference): this;
Per-pixel L2 RGB difference between this image and reference —
replaces this image with a grayscale heatmap of the distance.
Output: R = G = B = min(1, sqrt(dr² + dg² + db²) / sqrt(3)),
A = 1.0 (the /√3 normalisation maps black-vs-white to 1.0).
Both images must have the same dimensions; mismatched sizes leave the image untouched.
Phase 1a of the Hertzmann painterly pipeline (the "where does the canvas disagree with the reference" target for stroke placement). Independently useful for:
- Before/after diff visualisations.
- Quality-metric heatmaps (where MSE concentrates).
- Motion-detection gates ("did anything change?").
Parameters
| Parameter | Type | Description |
|---|---|---|
reference |
ImageHandle |
Image to compare against. |
Returns
this
this
Example
const before = Engine.loadImage(INPUT);
const after = before.clone().bilateralBlur(9, 0.1, 8);
const diff = before.clone().differenceMap(after);
diff.save(OUTPUT); // bright = the blur changed something
dilate()
dilate(size, region?): this;
Morphological dilation — expands bright regions.
Parameters
| Parameter | Type | Description |
|---|---|---|
size |
number |
Structuring element radius in pixels. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
dither()
dither(region?): this;
Floyd–Steinberg error-diffusion dithering (reduces to 1-bit per channel).
Parameters
| Parameter | Type |
|---|---|
region? |
Rect | RegionOpts |
Returns
this
this
drawText()
drawText(
text,
x,
y,
opts?): this;
Draw a single line of text onto the image at pixel (x, y).
The y-coordinate is the text baseline (SVG default), not
the top of the type. Two open-source variable fonts are
embedded with the engine and always available:
"Inter"(also responds to"sans-serif")"JetBrains Mono"(also"monospace") Custom fonts can be loaded withEngine.loadFont(path).
Parameters
| Parameter | Type | Description |
|---|---|---|
text |
string |
The string to draw. < > & are auto-escaped. |
x |
number |
X coordinate in pixels. |
y |
number |
Y baseline coordinate in pixels. |
opts? |
| MlcFont | { anchor?: "start" | "middle" | "end"; color?: Color; font?: string; lineHeight?: number; maxWidth?: number; size?: number; textAlign?: TextAlign; } |
- |
Returns
this
this
dropShadow()
dropShadow(
dx?,
dy?,
blur?,
color?,
strength?): this;
Drop shadow — a coloured, blurred, offset silhouette behind
the image. Composes tint → blur → blendAt at offset → blendAt sharp on top in one call.
Note: the image stays the same size — pixels of the shadow
that fall outside the original frame are clipped. If you
need a wider canvas, pad() first then call dropShadow.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
dx |
number |
5 |
Horizontal offset in pixels (positive → shadow to the right). Default 5. |
dy |
number |
5 |
Vertical offset in pixels (positive → down). Default 5. |
blur |
number |
5 |
Gaussian sigma of the shadow softness. Default 5. |
color |
Color |
'#000000' |
Shadow colour. Hex / Pixel / tuple. Default "#000000". |
strength |
number |
1.0 |
Shadow opacity, ∈ [0..1]. Default 1.0. |
Returns
this
this
Example
img.dropShadow(4, 4, 6, "#000000", 0.6);
img.pad(20, 20, 20, 20).dropShadow(8, 12, 10, "#000", 0.5);
emboss()
emboss(): this;
Emboss effect — classic relief kernel that makes the image look carved.
Applies the kernel [-2,-1,0, -1,1,1, 0,1,2], producing a grey midpoint
image that highlights directional edges.
Returns
this
this
erode()
erode(size, region?): this;
Morphological erosion — shrinks bright regions.
Parameters
| Parameter | Type | Description |
|---|---|---|
size |
number |
Structuring element radius in pixels. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
fillRectangle()
fillRectangle(
x,
y,
w,
h,
color): this;
Fill a rectangular region with a single colour, in-place. The rect is clipped to the image bounds; a fully off-screen rect is a silent no-op.
Accepts the same Color shapes as Engine.createColoredImage:
Pixel, hex string, or [r,g,b,(a)] array (0..1 floats).
Parameters
| Parameter | Type |
|---|---|
x |
number |
y |
number |
w |
number |
h |
number |
color |
Color |
Returns
this
Example
img.fillRectangle(10, 10, 100, 80, [1, 0, 0, 1]); // red box
img.fillRectangle(0, 0, img.width, img.height, "#ffffff"); // whiteout
filmGrain()
filmGrain(amount, region?): this;
Film grain — adds random noise for analog look.
Parameters
| Parameter | Type | Description |
|---|---|---|
amount |
number |
Noise intensity ∈ [0..1]. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
Note
Non-deterministic — result varies between runs.
fisheye()
fisheye(
center,
radius,
amount): this;
Fisheye lens distortion.
Parameters
| Parameter | Type | Description |
|---|---|---|
center |
Px |
Center of the distortion in pixel space, e.g. px(img.width/2, img.height/2). |
radius |
number |
Effect radius in pixels. |
amount |
number |
Distortion strength (positive = barrel, negative = pincushion). |
Returns
this
this
flipH()
flipH(): this;
Flip horizontally (mirror left↔right).
Returns
this
this
flipV()
flipV(): this;
Flip vertically (mirror top↔bottom).
Returns
this
this
forEachPixel()
forEachPixel(fn): this;
Iterate every pixel, calling fn(pixel, x, y).
If fn returns a Pixel, that value is written back.
⚠ Very slow for large images — intended for algorithmic / creative use.
Parameters
| Parameter | Type | Description |
|---|---|---|
fn |
(pixel, x, y) => void | Pixel |
`(pixel: Pixel, x: number, y: number) => Pixel |
Returns
this
this
free()
free(): void;
Release the underlying Rust image buffer. After calling free(), do not use this handle.
Calling free() explicitly is the deterministic option (memory
released immediately). When omitted, the FinalizationRegistry in
handle-registry.ts releases the buffer on the next GC pass — fine
for one-shot scripts, but for tight loops call .free() (or
Engine.gc() once per iteration) to keep peak RSS bounded.
Returns
void
gaussianBlur()
gaussianBlur(sigma, region?): this;
Gaussian blur.
Parameters
| Parameter | Type | Description |
|---|---|---|
sigma |
number |
Standard deviation in pixels. Higher = more blur. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
getMeta()
getMeta(key): unknown;
Read a single top-level metadata key. Returns the parsed value
(any JSON type) or null if the key is unset.
Parameters
| Parameter | Type |
|---|---|
key |
string |
Returns
unknown
Example
img.getMeta("opticscript").width; // sub-access via JS chaining
img.getMeta("dataset"); // user-set free-form value
getPixel()
getPixel(at): Pixel;
Read a single pixel. Returns a Pixel with r/g/b/a in [0..1].
Parameters
| Parameter | Type | Description |
|---|---|---|
at |
Px |
Pixel position, e.g. px(100, 50). |
Returns
glow()
glow(
blur?,
color?,
strength?): this;
Outer glow — soft luminous halo BEHIND the silhouette, extending past the alpha edges. Builds a tinted, blurred, alpha-faded copy of this image and stacks the sharp original on top so the body keeps its original colours and only the region outside the silhouette is coloured.
Implemented as a JS composition of existing primitives:
tint → blur → adjustAlpha(strength, Mul) → blendAt halo behind → blendAt sharp on top. Best on inputs with alpha
edges (post-rmbg, isolated subjects, rendered SVG, text).
On a fully opaque source the sharp top layer covers the
halo entirely — the call is effectively a no-op.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
blur |
number |
15 |
Gaussian sigma of the halo in pixels. Default 15. Larger = softer / wider halo. |
color |
Color |
'#ffffff' |
Halo tint colour. Hex string, Pixel, or [r,g,b,a] tuple. Default "#ffffff". |
strength |
number |
1.0 |
Halo opacity, ∈ [0..1]. Default 1.0. |
Returns
this
this
Example
img.glow(15, "#ffffff"); // classic white halo
img.glow(8, "#ff80ff", 0.6); // soft magenta border
grayscale()
grayscale(region?): this;
Convert to greyscale using luminance weights.
Parameters
| Parameter | Type |
|---|---|
region? |
Rect | RegionOpts |
Returns
this
this
hue()
hue(delta, region?): this;
Rotate hue by delta degrees.
Parameters
| Parameter | Type | Description |
|---|---|---|
delta |
number |
Degrees to shift hue, e.g. 180 inverts hue. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
invert()
invert(region?): this;
Invert all color channels (negative effect). Alpha is preserved.
Parameters
| Parameter | Type |
|---|---|
region? |
Rect | RegionOpts |
Returns
this
this
kaleidoscope()
kaleidoscope(
center,
segments,
rotation): this;
Kaleidoscope — mirror-slice symmetry.
Parameters
| Parameter | Type | Description |
|---|---|---|
center |
Px |
Center of symmetry in pixel space, e.g. px(img.width/2, img.height/2). |
segments |
number |
Number of mirror segments. |
rotation |
number |
Initial rotation offset in degrees. |
Returns
this
this
kuwahara()
kuwahara(radius, region?): this;
Classical Kuwahara filter — the modern "oil-painting" /
painterly effect. For each pixel the surrounding
(2·radius+1)² window is split into four overlapping
quadrants; the quadrant with the lowest luminance variance
wins and its mean RGB becomes the output. Result: flat
regions stay flat, edges snap, and the image gets that
characteristic "brush-zone" look without the cubist
colour-flattening that the older oilPaint (histogram-
based) introduces.
Recommended for new uses. radius 4–8 is a soft sketch,
10–20 a clear painting. Cost is O(W·H·radius²).
Parameters
| Parameter | Type | Description |
|---|---|---|
radius |
number |
Window half-extent in pixels (≥ 1). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
Example
img.kuwahara(8); // soft painterly look
img.bilateralBlur(7, 0.3, 12)
.kuwahara(12)
.saturation(1.15); // pre-smooth then paint
medianBlur()
medianBlur(radius): this;
Median filter — replaces each pixel's R/G/B channels with the
median of the channel values in a (2·radius+1)² window.
The classic salt-and-pepper denoiser: a single rogue
bright/dark pixel is gone after one pass because the median
ignores outliers that the mean would smear into neighbours.
For Gaussian-style sensor noise prefer bilateralBlur or
nonLocalMeans — those preserve fine texture better.
radius 1-2 typical for impulse cleanup; larger values lose
detail quickly. Cost O(W·H·radius²·log(radius)). Alpha
preserved.
Parameters
| Parameter | Type | Description |
|---|---|---|
radius |
number |
Window half-extent in pixels (≥ 1). |
Returns
this
this
mergeMeta()
mergeMeta(key, partial): this;
Shallow-merge a partial object into an existing metadata key.
If the key was unset or held a non-object, partial becomes
the new value. Convenience for the common
setMeta(k, { ...getMeta(k), ...partial }) pattern.
Parameters
| Parameter | Type |
|---|---|
key |
string |
partial |
Record<string, unknown> |
Returns
this
this
Example
img.mergeMeta("exif", { iso: 400, aperture: 2.8 });
meta()
meta(): Record<string, unknown>;
Return the full metadata blob attached to this image as a fresh
JS object. Auto-populated entries live under the opticscript.*
namespace (fileSize, mime, format, width, height, sourcePath, …);
EXIF / DICOM / TIFF parsers add their own siblings; user-set
data lives wherever you put it via setMeta.
The returned object is a snapshot — mutating it does not
affect storage. Use setMeta(key, value) to write back.
Returns
Record<string, unknown>
Example
const img = Engine.loadImage("photo.jpg");
const m = img.meta();
console.log(m.opticscript.mime); // "image/jpeg"
console.log(m.opticscript.fileSize); // 845231
montageH()
montageH(...others): ImageHandle;
Stack this image and one or more others side-by-side horizontally. The result height equals the tallest image; shorter images are top-aligned.
Parameters
| Parameter | Type | Description |
|---|---|---|
...others |
ImageHandle[] |
One or more ImageHandles to place to the right. |
Returns
New ImageHandle containing all images concatenated horizontally.
montageV()
montageV(...others): ImageHandle;
Stack this image and one or more others vertically. The result width equals the widest image; narrower images are left-aligned.
Parameters
| Parameter | Type | Description |
|---|---|---|
...others |
ImageHandle[] |
One or more ImageHandles to place below. |
Returns
New ImageHandle containing all images concatenated vertically.
motionBlur()
motionBlur(angleDeg, length): this;
Motion blur — directional linear blur along angleDeg for
length pixels. Simulates camera or subject motion. Single
pass; cost scales with the pixel length of the trail.
angleDeg is measured counter-clockwise from the +x axis:
0 blurs horizontally, 90 vertically, 45 along the
diagonal.
Parameters
| Parameter | Type | Description |
|---|---|---|
angleDeg |
number |
Direction of motion in degrees. |
length |
number |
Trail length in pixels (0 = no-op). |
Returns
this
this
Example
// 30° camera shake, 24 px trail.
img.motionBlur(30, 24);
// Pure horizontal motion (race car).
img.motionBlur(0, 40);
nonLocalMeans()
nonLocalMeans(
searchRadius,
patchRadius,
h): this;
Non-Local Means denoiser — the gold-standard for natural-photo
Gaussian noise. For each pixel the filter searches a window of
nearby pixels and averages them, weighting each neighbour by
patch similarity (small patch around centre vs. small
patch around candidate). Similar patches occur naturally
throughout a clean image but noise differs sample-to-sample,
so the weighted average preserves real structure (edges,
texture) while cancelling the noise. Markedly cleaner output
than bilateralBlur for sensor noise.
Integral-image accelerated — cost is O(W·H·search²) regardless of patch size; ~2-5 s for a 1500×1000 image at default params (search=7 / patch=3 / h=0.08).
Parameters
| Parameter | Type | Description |
|---|---|---|
searchRadius |
number |
Outer search-window half-extent (typical 5-10). |
patchRadius |
number |
Patch half-extent for similarity (typical 2-3). |
h |
number |
Filtering parameter; larger = more aggressive smoothing (typical 0.05-0.15 for [0..1] data). |
Returns
this
this
Example
img.nonLocalMeans(7, 3, 0.08); // strong denoise
img.nonLocalMeans(5, 2, 0.05); // gentle, fast
oilPaint()
oilPaint(
radius,
levels,
region?): this;
Oil paint effect (legacy histogram-based) — quantizes to
dominant colors in local windows. Kept for backward
compatibility; new code should prefer kuwahara(radius)
which produces a softer, more painterly look without the
cubist colour-flattening this method introduces.
Parameters
| Parameter | Type | Description |
|---|---|---|
radius |
number |
Local window radius in pixels. |
levels |
number |
Number of intensity levels (e.g. 8). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
Deprecated
Prefer kuwahara(radius) for new code.
orientationField()
orientationField(smoothSigma?): this;
Replace this image with an HSV-encoded visualisation of its local edge orientation, derived from the smoothed structure tensor on luminance:
- Hue = edge direction φ (mapped from [0, π] to a full colour wheel — same physical edge in either direction maps to the same hue).
- Saturation = coherence (
(λ₁ − λ₂) / (λ₁ + λ₂)) — high in clearly oriented regions, low in flat or noisy regions. - Value = 1.0 throughout, so flat regions read as bright grayscale and oriented regions burst into colour.
Phase 1b of the Hertzmann painterly pipeline. Same tensor math
as anisotropicKuwahara but exposed as a standalone op for
any flow-based effect. Visualisation is intentionally lossy —
a raw-vector variant will follow when stroke generation needs
precise vectors.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
smoothSigma |
number |
2.0 |
Gaussian sigma for tensor smoothing (typical 1.0–4.0). Larger = blockier, more coherent fields. Default 2.0. |
Returns
this
this
Example
const flow = Engine.loadImage(INPUT).orientationField(2.0);
flow.save(OUTPUT); // colour wheel = local edge direction
pad()
pad(
top,
bottom,
left,
right,
color?,
border?): this;
Pad the image with a border.
Updates width and height.
Parameters
| Parameter | Type | Description |
|---|---|---|
top |
number |
Top padding in pixels. |
bottom |
number |
Bottom padding in pixels. |
left |
number |
Left padding in pixels. |
right |
number |
Right padding in pixels. |
color? |
PadColor |
Border color: a Pixel, a CSS hex string ("#rrggbb" / "#rrggbbaa"), or a BorderMode number for non-constant modes (Reflect/Wrap/Replicate). Omit for transparent padding (default). |
border? |
BorderMode |
BorderMode override when color is a Pixel or string (default: Constant). |
Returns
this
this
padBottom()
padBottom(size, color?): this;
Pad bottom only. Shorthand for pad(0, size, 0, 0, color). Updates height.
Parameters
| Parameter | Type |
|---|---|
size |
number |
color? |
PadColor |
Returns
this
padLeft()
padLeft(size, color?): this;
Pad left side only. Shorthand for pad(0, 0, size, 0, color). Updates width.
Parameters
| Parameter | Type |
|---|---|
size |
number |
color? |
PadColor |
Returns
this
padRight()
padRight(size, color?): this;
Pad right side only. Shorthand for pad(0, 0, 0, size, color). Updates width.
Parameters
| Parameter | Type |
|---|---|
size |
number |
color? |
PadColor |
Returns
this
padTop()
padTop(size, color?): this;
Pad top only. Shorthand for pad(size, 0, 0, 0, color). Updates height.
Parameters
| Parameter | Type |
|---|---|
size |
number |
color? |
PadColor |
Returns
this
pencilSketch()
pencilSketch(strength): this;
Pencil-sketch effect — the classic "colour dodge" trick from
Photoshop / GIMP tutorials: convert to greyscale, invert,
blur, then divide the original grey by 1 - blurred_inverted.
Soft natural pencil strokes with the smooth tonal shading
graphite gives, no edge-detection harshness. Output is
greyscale; alpha preserved.
strength is the Gaussian sigma applied to the inverted
copy:
- 5–10 → tight, fine pencil hatching feel
- 15–30 → broader graphite shading
- < 1 → almost identical to plain greyscale
Parameters
| Parameter | Type | Description |
|---|---|---|
strength |
number |
Gaussian sigma for the inverted-blur step. |
Returns
this
this
Example
img.pencilSketch(20); // soft graphite
img.gaussianBlur(0.5)
.pencilSketch(15)
.brightness(1.05); // pre-soften then sketch
pixelate()
pixelate(size, region?): this;
Pixelate — mosaic effect.
Parameters
| Parameter | Type | Description |
|---|---|---|
size |
number |
Block size in pixels. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
posterize()
posterize(levels, region?): this;
Posterize — reduce to N discrete color levels per channel.
Parameters
| Parameter | Type | Description |
|---|---|---|
levels |
number |
Number of levels (e.g. 4 = retro poster look). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
premultiplyAlpha()
premultiplyAlpha(region?): this;
Pre-multiply RGB by alpha in-place. Rewrites (R, G, B, A) as
(R*A, G*A, B*A, A) so fully-transparent pixels carry RGB=0
and partially-transparent pixels are scaled correspondingly.
Use after rmbg/autocrop and before any geometry op that uses
bilinear interpolation (warpPerspective, stampAt, resize, …) to
stop background-colour bleed at silhouette edges. The original
RGB on transparent pixels (where rmbg only flipped α to 0)
would otherwise leak in at every soft edge.
Parameters
| Parameter | Type |
|---|---|
region? |
Rect | RegionOpts |
Returns
this
this
Example
Engine.loadImage(INPUT)
.rmbg()
.premultiplyAlpha()
.stampAt(bg, quad, 0.85);
rectFromCenter()
rectFromCenter(
at,
w,
h): Rect;
Build a normalized rect centered at a pixel position.
Parameters
| Parameter | Type | Description |
|---|---|---|
at |
Px |
Center position in pixel space. |
w |
number |
Width in pixels. |
h |
number |
Height in pixels. |
Returns
[x, y, w, h] all ∈ [0..1]
rectFromPixels()
rectFromPixels(
at,
w,
h): Rect;
Build a normalized [x, y, w, h] rect from a pixel origin and pixel dimensions.
Parameters
| Parameter | Type | Description |
|---|---|---|
at |
Px |
Top-left corner in pixel space. |
w |
number |
Width in pixels. |
h |
number |
Height in pixels. |
Returns
[x, y, w, h] all ∈ [0..1]
rectFull()
rectFull(): Rect;
Return a normalized rect covering the full image: [0, 0, 1, 1].
Returns
removeWhiteBackground()
removeWhiteBackground(opts?): this;
Knock out the bright background of a sketch / lineart by
driving alpha from luminance: pixels brighter than threshold
become transparent, darker pixels keep their colour and alpha.
RGB is preserved — only alpha changes.
Use this for black-on-white sketches where U²-Net (rmbg tool) struggles. Deterministic, no model — just luminance maths.
Parameters
| Parameter | Type | Description |
|---|---|---|
opts? |
{ feather?: number; threshold?: number; } |
- |
opts.feather? |
number |
Width of the soft edge in luminance units. 0 = hard binary cut, 0.05 = ~13/255 ramp. Default 0.05. |
opts.threshold? |
number |
Luminance cut-off ∈ [0..1]. Default 0.95 (≈ 242/255). Anything brighter goes transparent. |
Returns
this
this
resize()
resize(
w,
h,
interp?): this;
Resize to exact dimensions.
Updates width and height.
Parameters
| Parameter | Type | Description |
|---|---|---|
w |
number |
Target width in pixels. |
h |
number |
Target height in pixels. |
interp? |
Interp |
Interpolation method (default: Bilinear = 1). Use Interp.Nearest, Interp.Bilinear, or Interp.Bicubic. |
Returns
this
this
rotate()
rotate(angle): this;
Rotate in-place. Canvas stays the same size — corners are clipped.
Use rotateExpand() to expand the canvas instead.
Parameters
| Parameter | Type | Description |
|---|---|---|
angle |
number |
Angle in degrees (counter-clockwise). |
Returns
this
this
rotateExpand()
rotateExpand(angle): this;
Rotate and expand the canvas so the full image fits without clipping.
Updates width and height.
Parameters
| Parameter | Type | Description |
|---|---|---|
angle |
number |
Rotation angle in degrees (counter-clockwise). |
Returns
this
this
saturation()
saturation(factor, region?): this;
Adjust color saturation.
Parameters
| Parameter | Type | Description |
|---|---|---|
factor |
number |
0.0 = greyscale, 1.0 = unchanged, > 1.0 = vivid. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
save()
save(path, opts?): this;
Save the image to a file on disk (or to a registered output slot).
Parameters
| Parameter | Type | Description |
|---|---|---|
path |
string |
Absolute / relative filesystem path (e.g. "out.png", "result.jpg") OR a //!OUTPUT: slot name in CLI mode. |
opts? |
{ format?: "png" | "jpeg" | "webp" | "avif"; lossy?: boolean; quality?: number; } |
Optional encoder hints. Quality is in [0..100] and applies to lossy encoders (JPEG, lossy WebP, AVIF). lossy flips WebP between lossless (default) and lossy. format ("png" |
opts.format? |
"png" | "jpeg" | "webp" | "avif" |
- |
opts.lossy? |
boolean |
- |
opts.quality? |
number |
- |
Returns
this
this for chaining.
Throws
If the write fails.
img.save("out.png"); // PNG, lossless
img.save("out.webp"); // WebP, lossless (default)
img.save("out.webp", { quality: 82 }); // WebP, lossy q=82
img.save("out.webp", { lossy: true, quality: 90 });
img.save("out.jpg", { quality: 95 }); // JPEG q=95
img.save(OUTPUT, { format: "webp", quality: 82 }); // playground: override UI select
saveTIFF()
saveTIFF(keyOrPath): this;
Save this image as a one-page TIFF. Distinct from save() because
TIFF goes through a separate code path (the tiff crate, not the
image crate's TIFF feature) so we can share the encoder with
AnimationHandle.saveTIFF() and offer multi-page TIFF later from
the same call shape.
RGBA8, uncompressed — preserves alpha exactly, no quality loss.
Use this when you need a single-page TIFF for a workflow that
expects them (scientific imaging, archival, certain printers).
For animation-style multi-page TIFFs see AnimationHandle.saveTIFF.
Parameters
| Parameter | Type | Description |
|---|---|---|
keyOrPath |
string |
Output slot or filesystem path. |
Returns
this
sepia()
sepia(region?): this;
Apply sepia tone (warm brownish tint).
Parameters
| Parameter | Type |
|---|---|
region? |
Rect | RegionOpts |
Returns
this
this
setMeta()
setMeta(key, value): this;
Set a top-level metadata key. Value can be any JSON-serialisable type: primitive, array, or nested object. Overwrites whatever was there before.
To update only some fields of a nested object, use mergeMeta
or do the JS spread yourself:
img.setMeta("exif", { ...img.getMeta("exif"), iso: 200 }).
Parameters
| Parameter | Type |
|---|---|
key |
string |
value |
unknown |
Returns
this
this
Example
img.setMeta("dataset", "ct-batch-1");
img.setMeta("anon", { patientId: "OPAQUE-7F3A" });
setPixel()
setPixel(at, pixel): this;
Write a single pixel. Values are clamped to [0..1].
Parameters
| Parameter | Type | Description |
|---|---|---|
at |
Px |
Pixel position, e.g. px(100, 50). |
pixel |
Pixel |
A Pixel object. |
Returns
this
this for chaining.
shadowsHighlights()
shadowsHighlights(opts, region?): this;
Shadow / highlight recovery — tonal adjustment for photo editing.
Separates the image into dark and bright regions using luminance weighting and applies independent lift/suppression to each tonal range:
shadows > 0— lifts dark areas (opens under-exposed shadows).shadows < 0— crushes shadows (deepens blacks).highlights < 0— recovers blown highlights (pulls back bright areas).highlights > 0— brightens already-bright areas.
Both values are additive lifts ∈ [−1..1] weighted by (1 − L)² and L²
respectively, so the midtones are barely touched.
Parameters
| Parameter | Type | Description |
|---|---|---|
opts |
{ highlights?: number; shadows?: number; } |
- |
opts.highlights? |
number |
Highlight lift ∈ [−1..1]. Default 0 (no change). |
opts.shadows? |
number |
Shadow lift ∈ [−1..1]. Default 0 (no change). |
region? |
Rect | RegionOpts |
- |
Returns
this
this
Example
img.shadowsHighlights({ shadows: 0.25, highlights: -0.15 });
sharpen()
sharpen(amount, region?): this;
Unsharp-mask sharpening.
Parameters
| Parameter | Type | Description |
|---|---|---|
amount |
number |
Sharpening strength. 1.0 = moderate, > 2.0 = strong. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
sobel()
sobel(region?): this;
Sobel edge detection. Outputs edge magnitudes as greyscale.
Parameters
| Parameter | Type |
|---|---|
region? |
Rect | RegionOpts |
Returns
this
this
solarize()
solarize(threshold, region?): this;
Solarize — invert pixels above threshold (darkroom effect).
Parameters
| Parameter | Type | Description |
|---|---|---|
threshold |
number |
Inversion threshold ∈ [0..1]. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
stampAt()
stampAt(
src,
dstPoints,
factor?,
mode?,
interp?,
border?): this;
Perspective-place src onto this image at four destination corners.
Edge anti-aliasing is automatic. The warp samples src per
destination pixel; with the default WarpBorder.Transparent each
out-of-bounds bilinear tap contributes transparent, so a tilted
quad's silhouette interpolates opaque↔transparent and comes out
smooth. Pass border = WarpBorder.Clamp for hard edges (edge-
replicate); in that case pre-pad src with a 1-px transparent
border (src.pad(1,1,1,1)) if you still want a soft rim.
Parameters
| Parameter | Type | Description |
|---|---|---|
src |
ImageHandle |
The image to stamp. |
dstPoints |
[Px, Px, Px, Px] |
Four canvas-space corners [TL, TR, BR, BL] as Px values. |
factor? |
number |
Blend weight ∈ [0..1] (default 1.0). |
mode? |
BlendMode |
Blend mode (default BlendMode.Over). |
interp? |
Interp |
Interpolation mode (default Bilinear). |
border? |
WarpBorder |
Out-of-bounds mode (default WarpBorder.Transparent). |
Returns
this
this
stampGrid()
stampGrid(
content,
grid,
interp?,
factor?): this;
Forward complement of warpGrid: project content onto this image
through an R×C forward mesh.
Whereas warpGrid treats the nodes as the source UV at each uniform
destination grid point (inverse warp — useful for cylindrical/squeeze
effects on a single image), stampGrid treats them as the
destination position of each uniform source grid point — the
intuitive interpretation when you place a grid on a target surface in
the editor and want a second image to land inside it. Areas of the
base canvas not covered by any mesh cell keep their original pixels.
Internally, each of the (rows-1)×(cols-1) cells is rasterised as a
bilinear patch (inverse-bilinear per pixel inside the cell's bbox).
Adjacent cells share boundary nodes so they abut without gaps.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
content |
ImageHandle |
undefined |
Image to stamp onto this canvas. |
grid |
{ cols: number; nodes: Record<string, number[]>; rows: number; } |
undefined |
WarpGrid object. nodes are normalised to this image's size (x = nodes[r][c*2] * this.width). |
grid.cols |
number |
undefined |
- |
grid.nodes |
Record<string, number[]> |
undefined |
- |
grid.rows |
number |
undefined |
- |
interp |
number |
1 |
0 = nearest-neighbour, 1 = bilinear (default). |
factor |
number |
1.0 |
Global opacity multiplier ∈ [0..1] (default 1). |
Returns
this
this
Example
// Project a poster onto a curved monitor in `scene`:
const scene = Engine.loadImage(REFERENCE);
const poster = Engine.loadImage(CONTENT);
scene.stampGrid(poster, { rows: ROWS, cols: COLS, nodes: NODES });
scene.save(OUTPUT);
swirl()
swirl(
center,
radius,
angle): this;
Swirl / twirl distortion — pixels rotate around a center point.
Parameters
| Parameter | Type | Description |
|---|---|---|
center |
Px |
Center of the swirl in pixel space, e.g. px(img.width/2, img.height/2). |
radius |
number |
Swirl radius in pixels. Pixels beyond this are unaffected. |
angle |
number |
Maximum rotation angle in degrees at the center. |
Returns
this
this
threshold()
threshold(value, region?): this;
Binary threshold — pixels above value become white, below become black.
Parameters
| Parameter | Type | Description |
|---|---|---|
value |
number |
Threshold ∈ [0..1]. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
tint()
tint(
color,
strength,
region?): this;
Tint the image with a color.
Parameters
| Parameter | Type | Description |
|---|---|---|
color |
Color |
- |
strength |
number |
Blend strength ∈ [0..1]. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
toSVG()
toSVG(optsOrKey?): string;
Trace the image to SVG using vtracer.
Parameters
| Parameter | Type | Description |
|---|---|---|
optsOrKey |
string | SVGOptions |
Either an SVGOptions config object, or a string output key (writes the SVG to that output slot and also returns it). All options are optional — omitted fields use the defaults from SVGOptions. |
Returns
string
unsetMeta()
unsetMeta(key): this;
Delete a metadata key. No-op if the key was already absent.
Useful for stripping individual fields before export
(e.g. img.unsetMeta("exif.GPSInfo") to drop location).
Parameters
| Parameter | Type |
|---|---|
key |
string |
Returns
this
this
vignette()
vignette(strength, region?): this;
Apply vignette (edge darkening).
Parameters
| Parameter | Type | Description |
|---|---|---|
strength |
number |
0.0 = none, 1.0 = strong dark border. |
region? |
Rect | RegionOpts |
- |
Returns
this
this
warpGrid()
warpGrid(grid, interp?): this;
Apply a Catmull-Rom mesh warp defined by a WarpGrid object.
The grid's sparse nodes map is expanded to a dense flat array before
being passed to the Rust engine. Rows absent from nodes are filled
with their identity (evenly-spaced) UV positions.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
grid |
{ border?: WarpBorder; cols: number; nodes: Record<string, number[]>; rows: number; } |
undefined |
WarpGrid object (e.g. the WARPGRID global injected from the @warpgrid script block). |
grid.border? |
WarpBorder |
undefined |
- |
grid.cols |
number |
undefined |
- |
grid.nodes |
Record<string, number[]> |
undefined |
- |
grid.rows |
number |
undefined |
- |
interp |
number |
1 |
0 = nearest-neighbour, 1 = bilinear (default). |
Returns
this
this
warpPerspective()
warpPerspective(
dw,
dh,
matrix,
interp?,
border?): this;
Perspective (homography) warp.
Updates width and height.
The transform is src→dst (forward): for each source pixel it says where that pixel lands in the destination grid.
matrix accepts either:
- a Mat3 instance — the recommended path. Build one with
Matrix.Mat3.estimateSimilarity/Affine/Homography(...), or any gl-matrix construction. It is column-major; this wrapper transposes it to the engine's row-major layout for you. - a raw 9-element
number[]— treated as row-major[a,b,c, d,e,f, g,h,i]and passed straight through.
Out-of-bounds edges are anti-aliased by default: WarpBorder.Transparent
lets each bilinear tap that lands outside the source contribute
transparent, so a tilted quad's silhouette interpolates
opaque↔transparent instead of staircasing. Pass WarpBorder.Clamp
to replicate the edge texel instead (hard edges).
Parameters
| Parameter | Type | Description |
|---|---|---|
dw |
number |
Output width in pixels. |
dh |
number |
Output height in pixels. |
matrix |
number[] | Mat3 |
Mat3 or row-major number[9], src→dst forward. |
interp? |
Interp |
Interpolation method (default: Bilinear = 1). |
border? |
WarpBorder |
Out-of-bounds mode (default: WarpBorder.Transparent). |
Returns
this
this
warpPolar()
warpPolar(innerR?, outSize?): this;
Polar coordinate warp — maps the image onto a ring/donut shape.
The image's X axis maps to angle (0°→360°) and Y axis maps to radius.
Creates a "film roll" or "planet" effect.
Updates width and height.
Parameters
| Parameter | Type | Description |
|---|---|---|
innerR? |
number |
Inner ring radius as fraction of outer radius ∈ [0..1]. Default: 0.3. |
outSize? |
number |
Output image side length in pixels. 0 = auto (max of input dims). |
Returns
this
this
wave()
wave(
amplitudeX,
amplitudeY,
freqX,
freqY): this;
Sinusoidal wave distortion.
Parameters
| Parameter | Type | Description |
|---|---|---|
amplitudeX |
number |
Horizontal wave amplitude in pixels. |
amplitudeY |
number |
Vertical wave amplitude in pixels. |
freqX |
number |
Horizontal wave frequency (cycles per image width). |
freqY |
number |
Vertical wave frequency (cycles per image height). |
Returns
this
this