Search Docs
Loading...
Skip to content

Distortion Effects

Apply distortion effects to warp, shift, and transform images and videos for dynamic artistic visuals.

Distortion Effects

10 mins
estimated time
Download
StackBlitz
GitHub

Distortion effects differ from color filters in that they modify the geometry and spatial arrangement of pixels rather than their color values. CE.SDK provides several distortion effect types: liquid warping, mirror reflections, color channel shifting, radial pixelation, TV glitch, corner pin, bulge, wave warp, and displacement maps. The displacement map reads a second block as its map. Each effect offers configurable parameters to control the intensity and style of the distortion.

This guide covers how to enable distortion effects in the built-in UI and how to apply and configure them programmatically using the block API.

Using the Built-in Distortion UI#

To enable distortion effects in the inspector panel, use the Feature API:

// Enable effects in the inspector panel using the Feature API
cesdk.feature.enable('ly.img.effect');

Once enabled, users can access distortion effects from the inspector when selecting an image or video block. The effects panel displays available distortions with real-time preview as parameters are adjusted.

Check Effect Support#

Before applying distortion effects, verify the block supports them. Graphic blocks with image or video fills support effects, while scene blocks do not.

// Create a sample block to demonstrate effect support checking
const sampleBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, sampleBlock);
// Check if a block supports effects before applying them
const supportsEffects = engine.block.supportsEffects(sampleBlock);
console.log('Block supports effects:', supportsEffects);

Apply Liquid Effect#

The liquid effect creates organic, flowing distortions that warp the image as if viewed through water. We can configure the intensity and scale of the warping.

// Create an image block for liquid distortion demonstration
const liquidBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, liquidBlock);
// Create and apply liquid effect - creates flowing, organic warping
const liquidEffect = engine.block.createEffect('liquid');
engine.block.setFloat(liquidEffect, 'effect/liquid/amount', 0.5);
engine.block.setFloat(liquidEffect, 'effect/liquid/scale', 1.0);
engine.block.setFloat(liquidEffect, 'effect/liquid/time', 0.0);
engine.block.appendEffect(liquidBlock, liquidEffect);

The liquid effect parameters:

  • amount (0.0 to 1.0) - Controls the intensity of the warping
  • scale - Adjusts the size of the liquid pattern
  • time - Animation time offset for animated liquid distortions

Apply Mirror Effect#

The mirror effect reflects the image along a configurable side, creating symmetrical compositions.

// Create an image block for mirror effect demonstration
const mirrorBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, mirrorBlock);
// Create and apply mirror effect - reflects image along a side
const mirrorEffect = engine.block.createEffect('mirror');
// Side values: 0 = Left, 1 = Right, 2 = Top, 3 = Bottom
engine.block.setInt(mirrorEffect, 'effect/mirror/side', 0);
engine.block.appendEffect(mirrorBlock, mirrorEffect);

The side parameter uses integer values: 0 (Left), 1 (Right), 2 (Top), or 3 (Bottom) to specify the reflection axis.

Apply Shifter Effect#

The shifter effect displaces color channels at an angle, creating chromatic aberration commonly seen in glitch art and retro visuals.

// Create an image block for shifter effect demonstration
const shifterBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, shifterBlock);
// Create and apply shifter effect - displaces color channels
const shifterEffect = engine.block.createEffect('shifter');
engine.block.setFloat(shifterEffect, 'effect/shifter/amount', 0.3);
engine.block.setFloat(shifterEffect, 'effect/shifter/angle', 0.785);
engine.block.appendEffect(shifterBlock, shifterEffect);

The shifter effect parameters:

  • amount (0.0 to 1.0) - Controls the displacement distance
  • angle - Sets the direction of the shift in radians

Apply Radial Pixel Effect#

The radial pixel effect pixelates the image in a circular pattern emanating from the center, useful for focus effects or stylized treatments.

// Create an image block for radial pixel effect demonstration
const radialPixelBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, radialPixelBlock);
// Create and apply radial pixel effect - pixelates in circular pattern
const radialPixelEffect = engine.block.createEffect('radial_pixel');
engine.block.setFloat(radialPixelEffect, 'effect/radial_pixel/radius', 0.5);
engine.block.setFloat(
radialPixelEffect,
'effect/radial_pixel/segments',
0.5
);
engine.block.appendEffect(radialPixelBlock, radialPixelEffect);

The radial pixel effect parameters:

  • radius (0.0 to 1.0) - Controls the size of the pixelation effect
  • segments (0.0 to 1.0) - Controls the angular segmentation intensity

Apply TV Glitch Effect#

The TV glitch effect simulates analog television interference with horizontal distortion and rolling effects, popular for retro and digital aesthetics.

// Create an image block for TV glitch effect demonstration
const tvGlitchBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, tvGlitchBlock);
// Create and apply TV glitch effect - simulates analog TV interference
const tvGlitchEffect = engine.block.createEffect('tv_glitch');
engine.block.setFloat(tvGlitchEffect, 'effect/tv_glitch/distortion', 0.4);
engine.block.setFloat(tvGlitchEffect, 'effect/tv_glitch/distortion2', 0.2);
engine.block.setFloat(tvGlitchEffect, 'effect/tv_glitch/speed', 0.5);
engine.block.setFloat(tvGlitchEffect, 'effect/tv_glitch/rollSpeed', 0.1);
engine.block.appendEffect(tvGlitchBlock, tvGlitchEffect);

The TV glitch effect parameters:

  • distortion - Primary horizontal distortion intensity
  • distortion2 - Secondary distortion layer
  • speed - Animation speed for the glitch effect
  • rollSpeed - Vertical roll speed simulating signal sync issues

Apply Corner Pin Effect#

The corner pin effect moves the four corners of the block and stretches the image between them. Each corner position is relative to the block size: (0, 0) is the upper left corner and (1, 1) the lower right corner.

// Create an image block for corner pin demonstration
const cornerPinBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, cornerPinBlock);
// Move the two upper corners inward; positions are relative to the block size
const cornerPinEffect = engine.block.createEffect('corner_pin');
engine.block.setFloat(
cornerPinEffect,
'effect/corner_pin/upperLeftX',
0.25
);
engine.block.setFloat(cornerPinEffect, 'effect/corner_pin/upperLeftY', 0.2);
engine.block.setFloat(
cornerPinEffect,
'effect/corner_pin/upperRightX',
0.75
);
engine.block.setFloat(
cornerPinEffect,
'effect/corner_pin/upperRightY',
0.2
);
engine.block.appendEffect(cornerPinBlock, cornerPinEffect);

The corner pin effect has eight properties: upperLeftX, upperLeftY, upperRightX, upperRightY, lowerLeftX, lowerLeftY, lowerRightX and lowerRightY.

Apply Bulge Effect#

The bulge effect magnifies an elliptical area of the block. A negative height pinches the area instead.

// Create an image block for bulge demonstration
const bulgeBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, bulgeBlock);
// Magnify an ellipse in the center; the radii are in design units
const bulgeEffect = engine.block.createEffect('bulge');
engine.block.setFloat(bulgeEffect, 'effect/bulge/centerX', 0.5);
engine.block.setFloat(bulgeEffect, 'effect/bulge/centerY', 0.5);
engine.block.setFloat(
bulgeEffect,
'effect/bulge/horizontalRadius',
blockWidth * 0.4
);
engine.block.setFloat(
bulgeEffect,
'effect/bulge/verticalRadius',
blockHeight * 0.4
);
engine.block.setFloat(bulgeEffect, 'effect/bulge/height', 1.5);
engine.block.appendEffect(bulgeBlock, bulgeEffect);

The bulge effect parameters:

  • centerX, centerY (0.0 to 1.0) - The center of the ellipse, relative to the block size
  • horizontalRadius, verticalRadius - The radii of the ellipse in design units
  • height (-4.0 to 4.0) - The strength. Positive values magnify, negative values pinch

Apply Wave Warp Effect#

The wave warp effect moves the pixels of the block with a wave.

// Create an image block for wave warp demonstration
const waveWarpBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, waveWarpBlock);
// Height and width are in design units, so derive them from the block size
const waveWarpEffect = engine.block.createEffect('wave_warp');
engine.block.setEnum(waveWarpEffect, 'effect/wave_warp/waveType', 'Sine');
engine.block.setFloat(
waveWarpEffect,
'effect/wave_warp/height',
blockHeight * 0.05
);
engine.block.setFloat(
waveWarpEffect,
'effect/wave_warp/width',
blockWidth * 0.25
);
engine.block.appendEffect(waveWarpBlock, waveWarpEffect);

The wave warp effect parameters:

  • waveType - 'Sine', 'Square', 'Triangle' or 'Sawtooth'
  • height - The largest displacement in design units
  • width - The distance between two wave crests in design units
  • direction - The direction in which the wave travels, in degrees. 0 points up and 90 to the right
  • phase - The position in the wave cycle, in degrees

height and width are in design units, so they scale with the block. The example derives them from the block size.

Apply Displacement Map Effect#

The displacement map effect moves each pixel by the value of a second block, the map, at the same place. A value of 0.5 does not move the pixel. Higher and lower values move it up to maxHorizontal and maxVertical design units. horizontalChannel and verticalChannel pick the channel of the map that each direction reads.

// Create an image block for displacement map demonstration
const displacedBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, displacedBlock);
// The map is a hidden image block. The effect still reads it.
const mapBlock = await engine.block.addImage(imageUri, {
size: blockSize
});
engine.block.appendChild(page, mapBlock);
engine.block.setVisible(mapBlock, false);
const displacementMapEffect = engine.block.createEffect('displacement_map');
engine.block.setEnum(
displacementMapEffect,
'effect/displacement_map/horizontalChannel',
'Luminance'
);
engine.block.setFloat(
displacementMapEffect,
'effect/displacement_map/maxHorizontal',
blockWidth * 0.08
);
engine.block.setEnum(
displacementMapEffect,
'effect/displacement_map/verticalChannel',
'Luminance'
);
engine.block.setFloat(
displacementMapEffect,
'effect/displacement_map/maxVertical',
blockHeight * 0.08
);
engine.block.setEnum(
displacementMapEffect,
'effect/displacement_map/mapBehavior',
'Stretch'
);
engine.block.appendEffect(displacedBlock, displacementMapEffect);
// Set the map block as the source of the effect
engine.block.setEffectSource(displacementMapEffect, mapBlock);

Effect Sources#

setEffectSource() sets the block that an effect reads as its map, and null removes it. These rules apply to effect sources:

  • Only the displacement map effect reads a source.
  • A graphic, text or group block can be the source. The block that owns the effect can be its own source.
  • The effect reads the source without its position and rotation. It also reads a hidden source, so hide a block that you only use as a map.
  • The source still draws on the page when it is visible.
  • mapBehavior places the map on the block: 'Center', 'Stretch' or 'Tile'.
  • Without a source, the effect does not move any pixels.
  • setEffectSource() needs the appearance/effect scope on every block that the effect is attached to.

getEffectSource() returns the source block, or null when the effect has no source.

const effectSource = engine.block.getEffectSource(displacementMapEffect);
console.log('Effect source is the map block:', effectSource === mapBlock);

The source link takes part in the usual block operations:

  • Destroying the source clears the link. Undo restores it.
  • Undo and redo restore the source.
  • Saving and loading a scene keeps the source.
  • Duplicating the block that owns the effect keeps the same source. Duplicating a group with the owner and the source links the copy to the copied source.

List Applied Effects#

Retrieve all effects applied to a block to inspect or iterate over them.

// Get all effects applied to a block
const effects = engine.block.getEffects(tvGlitchBlock);
console.log('Applied effects:', effects);
// Get the type of each effect
effects.forEach((effect, index) => {
const effectType = engine.block.getType(effect);
console.log(`Effect ${index}: ${effectType}`);
});

This returns an array of effect IDs in the order they were applied.

Enable and Disable Effects#

Toggle effects on and off without removing them from the block. This preserves all effect parameters while controlling visibility.

// Check if an effect is enabled
const isEnabled = engine.block.isEffectEnabled(liquidEffect);
console.log('Liquid effect enabled:', isEnabled);
// Disable an effect without removing it
engine.block.setEffectEnabled(liquidEffect, false);
console.log(
'Liquid effect now disabled:',
!engine.block.isEffectEnabled(liquidEffect)
);
// Re-enable the effect
engine.block.setEffectEnabled(liquidEffect, true);

Disabled effects remain attached to the block but won’t be rendered until re-enabled. This is useful for before/after comparisons or performance optimization.

Remove Effects#

Remove effects from a block when they’re no longer needed. Always destroy removed effects to free memory.

// To remove an effect, get its index and use removeEffect
const shifterEffects = engine.block.getEffects(shifterBlock);
const effectIndex = shifterEffects.indexOf(shifterEffect);
if (effectIndex !== -1) {
// Remove effect at the specified index
engine.block.removeEffect(shifterBlock, effectIndex);
// Destroy the removed effect to free memory
engine.block.destroy(shifterEffect);
}
// Re-add the effect for display purposes
const newShifterEffect = engine.block.createEffect('shifter');
engine.block.setFloat(newShifterEffect, 'effect/shifter/amount', 0.3);
engine.block.setFloat(newShifterEffect, 'effect/shifter/angle', 0.785);
engine.block.appendEffect(shifterBlock, newShifterEffect);

Discover Effect Properties#

Use findAllProperties() to discover all available properties for any effect type.

// Find all available properties for an effect
const tvGlitchProperties = engine.block.findAllProperties(tvGlitchEffect);
console.log('TV glitch properties:', tvGlitchProperties);

This returns an array of property paths that can be used with setFloat(), setInt(), or setEnum().

Handling Errors#

Invalid input to setEffectSource() throws an error. Its code names the problem.

try {
// Only the displacement map effect reads a source
engine.block.setEffectSource(liquidEffect, mapBlock);
} catch (error) {
if (isEngineError(error)) {
console.log(error.code); // 'BLOCK.EFFECT_SOURCE_UNSUPPORTED'
}
}
try {
// A page cannot be a source
engine.block.setEffectSource(displacementMapEffect, page);
} catch (error) {
if (isEngineError(error)) {
console.log(error.code); // 'BLOCK.EFFECT_SOURCE_INVALID'
}
}

Troubleshooting#

  • The effect does not accept a source (BLOCK.EFFECT_SOURCE_UNSUPPORTED): only a displacement_map effect reads a source. getEffectSource() also throws for other effects.
  • The block cannot be a source (BLOCK.EFFECT_SOURCE_INVALID): use a graphic, text or group block. A page, scene, fill, shape or effect cannot be a source.
  • The displacement map does not move anything: set a source with setEffectSource(), and check that maxHorizontal or maxVertical is not 0.
  • The effect is not visible: check that the block supports effects and that the effect is enabled.

API Reference#

Method Description
engine.block.supportsEffects(id) Check if a block supports effects
engine.block.createEffect(type) Create a new effect instance
engine.block.appendEffect(id, effectId) Add an effect to a block
engine.block.getEffects(id) Get all effects applied to a block
engine.block.setEffectEnabled(effectId, enabled) Enable or disable an effect
engine.block.isEffectEnabled(effectId) Check if an effect is enabled
engine.block.removeEffect(id, index) Remove an effect at a specific index
engine.block.findAllProperties(id) Discover all properties of an effect
engine.block.setFloat(id, property, value) Set a float property value
engine.block.setInt(id, property, value) Set an integer property value
engine.block.setEnum(id, property, value) Set an option property, such as effect/wave_warp/waveType
engine.block.setEffectSource(effectId, source) Set or remove the block that a displacement map effect reads
engine.block.getEffectSource(effectId) Get the block that a displacement map effect reads
engine.block.destroy(id) Destroy a block to free memory
engine.block.getType(id) Get the type of a block

Available Distortion Effects#

Effect Type Description Key Properties
liquid Flowing, organic warping amount, scale, time
mirror Reflection along a side side (0=Left, 1=Right, 2=Top, 3=Bottom)
shifter Chromatic aberration amount, angle
radial_pixel Circular pixelation radius, segments
tv_glitch Analog TV interference distortion, distortion2, speed, rollSpeed
corner_pin Moves the four corners upperLeftX, upperLeftY, upperRightX, upperRightY, lowerLeftX, lowerLeftY, lowerRightX, lowerRightY
bulge Magnifies or pinches an elliptical area centerX, centerY, horizontalRadius, verticalRadius, height
wave_warp Moves pixels with a wave waveType, height, width, direction, phase
displacement_map Moves pixels by the values of a source block horizontalChannel, maxHorizontal, verticalChannel, maxVertical, mapBehavior, wrapPixels

Next Steps#