Image processing
Apply effects to a user's image with an editing extension.
The basic lifecycle of an editing extension has three steps:
  1. 1.
    Receive an image from Canva.
  2. 2.
    Perform some kind of processing on the image.
  3. 3.
    Return the manipulated image to Canva.
All editing extensions perform these steps.
This guide explains how to create an editing extension that implements each step in this lifecycle. This lays the groundwork for developing extensions with more advanced features, such as rich controls and presets.

Prerequisites

  • You're comfortable with JavaScript.
  • You know what an editing extension is.
  • You've read the Quick start guide.

Step 1: Initialize an editing extension

When a user opens an editing extension, Canva:
  1. 1.
    Places an iframe on top of the user's image.
  2. 2.
    Injects a JavaScript client into the iframe.
  3. 3.
    Attaches the client to the window object.
An extension can use the client to interact with Canva.
To initialize the client:
  1. 1.
    Call the init method:
    1
    const canva = window.canva.init();
    Copied!
    This method returns a EditingExtensionClient.API object, which provides methods for interacting with Canva.
  2. 2.
    Register a callback with the onReady method:
    1
    const canva = window.canva.init();
    2
    3
    canva.onReady(async (opts) => {
    4
    console.log(opts);
    5
    });
    Copied!
    This callback runs when the extension loads. It receives an opts object that contains information the extension needs to render and manipulate the user's image.

Step 2: Render the user's image

When a user opens an extension, the extension must download and render the user's image in the iframe. If it doesn't, the iframe remains empty and the user's image appears to vanish.
To let an extension download the user's image, Canva passes an opts.element parameter into the onReady callback. This parameter is a CanvaElement object. It contains metadata about how the image appears in the design (for example, position and rotation) and URLs for downloading the user's image.
An extension can download the user's image in three different resolutions:
  • thumbnail
  • preview
  • full
In general, extensions should download the preview version of the user's image. Compared to the full version, the average user won't notice a difference in terms of visual clarity, but the extension will be more performant.
To download and render the user's image:
  1. 1.
    Destructure imageHelpers from the window.canva object:
    1
    const { imageHelpers } = window.canva;
    Copied!
    The imageHelpers object provides helper methods for common image operations.
  2. 2.
    Pass the opts.element parameter into the fromElement method:
    1
    canva.onReady(async (opts) => {
    2
    // Convert the CanvaElement into a CanvaImageBlob
    3
    const image = await imageHelpers.fromElement(opts.element, "preview");
    4
    });
    Copied!
    This downloads the user's image and converts it into a CanvaImageBlob. The second argument of "preview" tells Canva to download the preview version of the image. (The default value is "full".)
  3. 3.
    Use the toCanvas method to convert the CanvaImageBlob into an HTMLCanvasElement:
    1
    canva.onReady(async (opts) => {
    2
    // Convert the CanvaElement into a CanvaImageBlob
    3
    const image = await imageHelpers.fromElement(opts.element, "preview");
    4
    5
    // Convert the CanvaImageBlob into an HTMLCanvasElement
    6
    const canvas = await imageHelpers.toCanvas(image);
    7
    });
    Copied!
  4. 4.
    Append the HTMLCanvasElement to the document.body:
    1
    canva.onReady(async (opts) => {
    2
    // Convert the CanvaElement into a CanvaImageBlob
    3
    const image = await imageHelpers.fromElement(opts.element, "preview");
    4
    5
    // Convert the CanvaImageBlob into an HTMLCanvasElement
    6
    const canvas = await imageHelpers.toCanvas(image);
    7
    8
    // Render the HTMLCanvasElement
    9
    document.body.appendChild(canvas);
    10
    });
    Copied!
    This renders the HTMLCanvasElement in the iframe.
  5. 5.
    Call the updateControlPanel method:
    1
    canva.onReady(async (opts) => {
    2
    // Convert the CanvaElement into a CanvaImageBlob
    3
    const image = await imageHelpers.fromElement(opts.element, "preview");
    4
    5
    // Convert the CanvaImageBlob into an HTMLCanvasElement
    6
    const canvas = await imageHelpers.toCanvas(image);
    7
    8
    // Render the HTMLCanvasElement
    9
    document.body.appendChild(canvas);
    10
    11
    // Render the control panel
    12
    canva.updateControlPanel([]);
    13
    });
    Copied!
    This method is required even if the extension doesn't render rich controls. Without it, the control panel remains in a loading state.
After making these changes, preview the extension and see the user's image appear in the iframe.

Step 3: Apply an effect to the user's image

An editing extension can apply effects to the user's image at various points in its lifecycle, such as when:
  • The extension loads.
  • The user selects a preset.
  • The user interacts with a rich control.
Canva doesn't care how an extension applies effects to the user's image, but some common tools include:
The following snippet uses the Canvas API to invert the colors of the user's image when the extension loads:
1
canva.onReady(async (opts) => {
2
// Convert the CanvaElement into a CanvaImageBlob
3
const image = await imageHelpers.fromElement(opts.element, "preview");
4
5
// Convert the CanvaImageBlob into a HTMLCanvasElement
6
const canvas = await imageHelpers.toCanvas(image);
7
8
// Render the user's image in the iframe
9
document.body.appendChild(canvas);
10
11
// Get a 2D drawing context
12
const context = canvas.getContext("2d");
13
14
// Invert the colors of the user's image
15
context.filter = "invert(100%)";
16
17
// Draw the inverted image into the HTMLCanvasElement
18
context.drawImage(canvas, 0, 0, canvas.width, canvas.height);
19
20
// Render the control panel. (This is always required, even if
21
// the extension doesn't have rich controls.)
22
canva.updateControlPanel([]);
23
});
Copied!
This is what the extension looks like:

Step 4: Update the user's image

Even though extensions should download the preview version of the user's image when the extension loads, extensions must always save the highest possible resolution of the user's image.
To accomplish this, Canva calls the onImageUpdate callback immediately before attempting to save the user's image. This callback receives the full-resolution version of the user's image via the opts.image parameter. Upon receiving this request, an extension must:
  • Replace the original image with the updated image.
  • Re-apply all effects to the updated image.
To handle image update requests:
  1. 1.
    Register a callback with the onImageUpdate method:
    1
    canva.onImageUpdate(async (opts) => {
    2
    console.log(opts);
    3
    });
    Copied!
  2. 2.
    Use the toImageElement method to convert the CanvaImageBlob into an HTMLImageElement:
    1
    canva.onImageUpdate(async (opts) => {
    2
    // Convert the CanvaImageBlob into an HTMLImageElement
    3
    const img = await imageHelpers.toImageElement(opts.image);
    4
    });
    Copied!
  3. 3.
    Draw the updated image into the existing HTMLCanvasElement:
    1
    canva.onImageUpdate(async (opts) => {
    2
    // Convert the CanvaImageBlob into an HTMLImageElement
    3
    const img = await imageHelpers.toImageElement(opts.image);
    4
    5
    // Get the current HTMLCanvasElement
    6
    const canvas = document.querySelector("canvas");
    7
    8
    // Draw the HTMLImageElement into the HTMLCanvasElement
    9
    canvas.drawImage(img, 0, 0, canvas.width, canvas.height);
    10
    });
    Copied!
    In this case, the extension doesn't need to re-apply the invert filter, because the context.filter property is attached to the HTMLCanvasElement. In a more advanced extension though, the logic for applying an effect is often refactored into a separate function and reused throughout the extension's lifecycle.

Step 5: Save the user's image

If the user user closes an extension without clicking the Cancel button, Canva calls the onSaveRequest callback. In this callback, the extension must return the manipulated image to Canva as a CanvaImageBlob. Canva can then persist the changes the user has made to their image.
To handle save requests:
  1. 1.
    Register a callback with the onSaveRequest method:
    1
    canva.onSaveRequest(async () => {
    2
    // code goes here
    3
    });
    Copied!
  2. 2.
    Use the fromCanvas method to return the user's image as a CanvaImageBlob:
    1
    canva.onSaveRequest(async () => {
    2
    // Get the HTMLCanvasElement
    3
    const canvas = document.querySelector("canvas");
    4
    5
    // Convert the HTMLCanvasElement into a CanvaImageBlob
    6
    return await imageHelpers.fromCanvas("image/jpeg", canvas);
    7
    });
    Copied!
    You can also return the user's image as a CanvaElement.

Example

1
// Get helper methods for working with images
2
const { imageHelpers } = window.canva;
3
4
// Initialize the client
5
const canva = window.canva.init();
6
7
// The extension has loaded
8
canva.onReady(async (opts) => {
9
// Convert the CanvaElement into a CanvaImageBlob
10
const image = await imageHelpers.fromElement(opts.element, "preview");
11
12
// Convert the CanvaImageBlob into a HTMLCanvasElement
13
const canvas = await imageHelpers.toCanvas(image);
14
15
// Render the user's image in the iframe
16
document.body.appendChild(canvas);
17
18
// Get a 2D drawing context
19
const context = canvas.getContext("2d");
20
21
// Invert the colors of the user's image
22
context.filter = "invert(100%)";
23
24
// Draw the inverted image into the HTMLCanvasElement
25
context.drawImage(canvas, 0, 0, canvas.width, canvas.height);
26
27
// Render the control panel. (This is always required, even if
28
// the extension doesn't have rich controls.)
29
canva.updateControlPanel([]);
30
});
31
32
// Canva has requested the extension to update the user's image
33
canva.onImageUpdate(async (opts) => {
34
// Get the updated image
35
const img = await imageHelpers.toImageElement(opts.image);
36
37
// Get the HTMLCanvasElement that contains the user's image
38
const canvas = document.querySelector("canvas");
39
40
// Get a 2D drawing context
41
const context = canvas.getContext("2d");
42
43
// Draw the updated image into the HTMLCanvasElement
44
context.drawImage(img, 0, 0, canvas.width, canvas.height);
45
});
46
47
// Canva has requested the extension to save the user's image
48
canva.onSaveRequest(async () => {
49
// Get the HTMLCanvasElement that contains the user's image
50
const canvas = document.querySelector("canvas");
51
52
// Return the image to Canva as a CanvaImageBlob
53
return await imageHelpers.fromCanvas("image/jpeg", canvas);
54
});
Copied!
Last modified 1mo ago