The CreativeEditor SDK (CE.SDK) ships with a headless Server Mode for Node.js that lets you process scenes without rendering a UI. This guide covers how to script precise rotations against CE.SDK’s server APIs so your Node.js services can rotate assets on demand.
Requirements#
- Node.js 18 or newer
- CE.SDK package in your app:
npm install @cesdk/node CESDK_BASE_URLpointing to your CE.SDK asset bundle (path or HTTPS)
What You’ll Learn#
- Rotate image blocks by precise angles with JavaScript.
- Apply the same rotation to multiple blocks in one pass.
- Restore the original orientation or limit rotation to specific increments.
When to use#
Image rotation is a frequent step in backend workflows. Use server-side rotation when you need to:
- Normalize user uploads before downstream processing.
- Generate asset variations (for example, preview thumbnails) on your backend.
- Enforce template rules during automated rendering jobs.
Use these server-side rendering patterns to run asset generation and template enforcement jobs without opening the client editor.
How to Use#
Test a sample file
- At the root of your Node.js project, run
npm install @cesdk/node. - Add your license key to
.env:LICENSE_KEY="<your_license_key>". - Create a file named
rotate.js. - Paste the following script into
rotate.js:
import 'dotenv/config';import fs from 'fs/promises';import path from 'path';import CreativeEngine from '@cesdk/node';
// Configuration for the engineconst config = { license: process.env.LICENSE_KEY, baseURL: 'https://cdn.img.ly/packages/imgly/cesdk-node/1.60.0/assets'};
// Start CreativeEngineCreativeEngine.init(config).then(async (engine) => { console.log('CE.SDK Engine initialized');
// Load the demo scene from IMG.LY CDN try { await engine.addDefaultAssetSources(); await engine.scene.loadFromURL( 'https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_instagram_photo_1.scene' );
// Grab the first page const [page] = engine.block.findByType('page');
// Collect every graphic block const graphics = engine.block.findByType('graphic'); if (graphics.length === 0) { throw new Error('No graphic blocks found to rotate.'); }
// Loop through the graphics and rotate each one by 45° graphics.forEach((blockId) => { engine.block.setFloat(blockId, 'rotation', Math.PI / 4); });
// Export the updated page to a PNG const blob = await engine.block.export(page, { mimeType: 'image/png' }); // Convert the blob to a buffer const arrayBuffer = await blob.arrayBuffer();
// Set an output directory const outputDir = path.resolve('.'); await fs.mkdir(outputDir, { recursive: true });
const pattern = /^example-output\((\d+)\)\.png$/; const nextIndex = (await fs.readdir(outputDir)) .map((file) => pattern.exec(file)?.[1]) .filter(Boolean) .map(Number) .reduce((max, n) => Math.max(max, n), 0) + 1; // Pick and output filename const outputName = `example-rotated(${nextIndex}).png`; const outputPath = path.join(outputDir, outputName);
// Write the buffer to disk await fs.writeFile(outputPath, Buffer.from(arrayBuffer)); console.log(`Export completed: ${outputName}`); } catch (error) { console.error('Error processing scene:', error); } finally { engine.dispose(); }});- Run
node rotate.js. - The script downloads a sample, rotates it by 45°, and stores the export alongside the script (change the output location by updating
const outputDir).
- After starting the CreativeEditor and loading the scene from the image, return every page in the scene:
const [page] = engine.block.findByType('page');- Collect all graphic blocks on the page:
const graphics = engine.block.findByType('graphic');if (graphics.length === 0) { throw new Error('No graphic blocks found to rotate.');}- Check every graphic block ID:
graphics.forEach((blockId) => { // Rotation function});Now, you’re ready to call the rotation functions.
Rotate an image#
Combine the setRotation function with Math.PI. For example, to rotate an image by 45°:
engine.block.setRotation(blockId, Math.PI / 4);Rotate multiple blocks together#
Grouping keeps relative positions intact when you need to rotate blocks together.
const groupId = engine.block.group(graphics);engine.block.setFloat(groupId, 'rotation', Math.PI / 4);If you run this inside a batch job, and later jobs need different rotations, remember to either:
- Ungroup the scene.
- Duplicate the scene.
To rotate every graphic block in the scene without grouping:
const graphics = engine.block.findByType('graphic');if (graphics.length === 0) { throw new Error('No graphic blocks found to rotate.');}
graphics.forEach((blockId) => { engine.block.setFloat(blockId, 'transform/rotation', Math.PI / 4);});Reset rotation to the original orientation#
engine.block.setFloat(blockId, 'rotation', 0);Use this to revert to the original orientation after exporting an alternate angle.
Snap rotation to fixed increments#
Snap the rotation to fixed increments before saving the scene for later editing:
graphics.forEach((blockId) => { const targetAngle = Math.PI / 3; // 60° in radians const step = Math.PI / 2; const snapped = Math.round(targetAngle / step) * step;
engine.block.setFloat(blockId, 'rotation', snapped); // Rotation function});Valid steps include 15deg, 45deg, 90deg, or 360deg (no constraint).
Troubleshooting#
| Issue | Resolution |
|---|---|
| Image fails to load | Verify the URI is reachable from your server or switch to a file URI inside the asset bundle. |
| Rotation has no effect | Ensure your code appends the block to a page before setting rotation. |
| Engine keeps running | Call engine.dispose() once exports complete to free native resources. |
Next steps#
- Chain multiple transforms such as scaling or cropping in the same job.
- Export the rotated scene to PNG or JPEG for delivery.
- Explore template automation to combine rotation with additional constraints.
API reference
| API Function | Usage |
|---|---|
| block.findByType | Locate pages or block types before applying transforms. |
| block.group | Group blocks for collective transforms. |
| block.setFloat | Set numeric transforms (use ‘transform/rotation’ for single blocks, ‘rotation’ for groups). |
| block.export | Render a page to an image or document. |
| scene.create | Start a new scene when running headless jobs. |