Skip to main content

API Module

Pro Feature

Pro features are only available with a Professional licence. To upgrade, visit cavalry.scenegroup.co.

Introduction

The following APIs are all in the api namespace. They are ONLY available in the JavaScript Editor and not available when writing expressions in the JavaScript Utility.

All commands in this namespace must be prefixed with api. e.g api.create("null", "My Null");

The API can communicate with the Web (see Web APIs) and execute shell commands. See runProcess below.

tip

When working with layers and attributes it is important to know their layerId and attrId.

To find the layerId:

  1. Right click on a layer in the Viewport, Scene Window or its header bar in the Attribute Editor.
  2. Select Copy Layer Id.

This will copy the layer's Id to the clipboard.

To find the attrId:

  1. Right click on an attribute in the Attribute Editor.
  2. Choose Copy Scripting Path from the contextual menu.
  3. Select and click an option from the list.

This will copy the attribute's Id to the clipboard.

These Ids can then be used within a script. In the example below, basicShape#1 is the layerId and position.x is the attrId.

api.get("basicShape#1", "position.x");

Member Functions

Working with the Composition

setFrame(frame:int)

Move the playhead to a specific frame.

// Move the playhead to frame 100
api.setFrame(100);

getFrame() → int

Return the frame number the playhead is on.

// Move the playhead to frame 50 and then print that frame number to the message bar
api.setFrame(50);
console.log(api.getFrame());

play()

Start playback.

getCompLayers(isTopLevel:bool) → string[]

var primId = api.primitive("polygon", "My Polygon");
var nullId = api.create("null", "My Null");
api.parent(primId, nullId);
// The boolean indicates top level layers only (ie. ignore all children)
var topLevelIds = api.getCompLayers(false);
// Prints: 1
console.log(topLevelIds.length)
// Get all the layers in the composition
var allIds = api.getCompLayers(true);
// Prints: 2
console.log(allIds.length)

getCompLayersOfType(isTopLevel:bool, type:string) → string[]

Return all the Layers in the active Composition of a certain type.

api.create("null", "My Null");
api.create("null", "My Other Null");
api.create("group", "My Folder");
api.create("spreadsheet", "My Spreadsheet");
let nulls = api.getCompLayersOfType(false, "null");
for (var layer of nulls) {
console.log(api.getNiceName(layer));
}

createTimeMarker(time:int) → string

Adds a new Time Marker. The second example sets up a Time Marker as a controller for a Scheduling Group.

let markerId = api.createTimeMarker(10);
api.set(markerId, {"label": "Hello, World", "drawColor": "Pink", "useRelPlacement": true});
/// After running this script, move the Time Marker around
let rectId = api.primitive("rectangle", "My Rectangle");
let ellipseId = api.primitive("ellipse", "My Ellipse");

let scheduleGroup = api.create("schedulingGroup", "Marker Controlled Group");
api.parent(rectId, scheduleGroup);
api.parent(ellipseId, scheduleGroup);

let markerId = api.createTimeMarker(10);
api.set(markerId, {"label": "Hello, World", "drawColor": "Pink", "useRelPlacement": true});

api.connect(markerId, "id", scheduleGroup, "childOffset");

getTimeMarkers() → string[]

Returns a list of all the Time Marker Ids in this Composition

api.createTimeMarker(10);
api.createTimeMarker(40);
var markers = api.getTimeMarkers();
for (let markerId of markers) {
console.log(markerId)
}

removeTimeMarker(markerId:string)

A convenience function for removing Time Markers. This forwards to api.Layer(layerId).

getNthBeat(beat:int) → int

Return the frame of the 'n'th beat from the Beat Marker settings. For example, an argument of 3 will return the frame number that the 3rd beat falls on.

const frame = api.getNthBeat(3);
console.log(frame);

addGuide(compId:string, isVertical:bool, position:int) → int

Add a Ruler Guide to the given Composition. This function returns an id which can be used to delete the Guide later. Note that 0,0 is the centre of the Composition.

let id = api.addGuide(api.getActiveComp(), false, 100);
console.log(id);

deleteGuide(compId:string, id:int)

Delete a Ruler Guide with the corresponding id from the given Composition.

let id = api.addGuide(api.getActiveComp(), false, 100);
console.log(id);
api.deleteGuide(api.getActiveComp(), 1);
// If you run this in a new scene, there should be no guides.

clearGuides(compId:string)

Clear all Ruler Guide from the given Composition.

api.addGuide(api.getActiveComp(), true, -100);
api.addGuide(api.getActiveComp(), false, 100);
api.clearGuides(api.getActiveComp());
// If you run this in a new scene, there should be no guides.

getGuideInfo(compId:string) → array[{id:int, direction:int, position:int}]

Get the Ids of all the Ruler Guides in the given Composition.

Direction returns:

  • 0 for Horizontal Guides.
  • 1 for Vertical Guides.
api.addGuide(api.getActiveComp(), true, -100);
api.addGuide(api.getActiveComp(), false, 100);
console.log(JSON.stringify(api.getGuideInfo(api.getActiveComp())));

getControlCentreAttributes(compId:string) → [string]

Get a list of all the Attributes that have been added to the Control Centre.

console.log(api.getControlCentreAttributes(api.getActiveComp()));

timecodeToFrames(timecode:string, fps:number) → int

Converts a given frame number into an equivalent timecode based on a given frame rate. Note that a timecode starts at frame 0 regardless of the Frame Range set in the Composition Settings.

console.log(api.timecodeToFrames("00:00:06:05",30));

framesToTimecode(frame:int, fps:number) → string

Converts a given timecode into an equivalent frame number based on a given frame rate. Note that a timecode starts at frame 0 regardless of the Frame Range set in the Composition Settings.

console.log(api.framesToTimecode(100,25));

Working with Layers

primitive(type:string, name:string) → string

Creates a Primitive Shape.

/// returns the layerId for the new shape
var primId = api.primitive("rectangle", "My Rectangle");

createEditable(path:Path, name:string) → string

Creates an Editable Shape from a Path.

var path = new cavalry.Path();
path.moveTo(0.,0.);
path.lineTo(0.,-100.);
path.lineTo(300.,-100.);
path.cubicTo(210., 110., 240., 140., 280., 260);
path.close();
api.createEditable(path, "My Path");

create(layerType:string, name:string) → string

Creates a Layer of any type. The optional name argument specifies the name of the Layer in the Scene Window.

api.create("null", "My Null");

deleteLayer(layerId:string)

Delete a Layer.

/// Delete all render queue items
var items = api.getRenderQueueItems();
for (var layer of items) {
api.deleteLayer(layer);
}

layerExists(layerId:string) → bool

Returns true if a layer with the given layerId exists.

var layerId = api.create("basicShape","Layer")
console.log("Layer exists:" + api.layerExists(layerId) + ". Active Comp exists: " + api.layerExists(api.getActiveComp()));

getLayerType(layerId:string) → string

Get the layer's type (which can be used to create new instances of this layer).

var layerId = api.create("null", "My Null");
console.log(api.getLayerType(layerId));

resetLayerAttributes(layerId:string)

Reset all Attributes on a layer back to the default state.

getSelection() → string[]

Gets the currently selected Layers from the Composition or Assets Window.

// Print the selected Layer's nice names to the console.
var sel = api.getSelection();
for (var layer of sel) {
console.log(api.getNiceName(layer));
}

select(layers:string[])

Sets the selected Layers.

var primId = api.primitive("rectangle", "My Rectangle");
api.select([primId]);

invertSelection()

Deselect any selected Layers and select any deselected Layers.

var rect1 = api.primitive("rectangle", "Selected");
var rect2 = api.primitive("rectangle", "Not Selected");
api.select([rect2]);
api.invertSelection();

duplicate(layerId:string, withInputConnections:bool);

Duplicate a Layer with an option to also duplicate any input connections.

getChildren(layerId:string) → string[]

Gets the children of the specified layer.

var primId = api.primitive("polygon", "My Polygon");
var nullId = api.create("null", "My Null");
api.parent(primId, nullId);
var childIds = api.getChildren(nullId);
console.log(childIds.length)

parent(layerId:string, newParentId:string)

Make one Layer the child of another.

var primId = api.primitive("polygon", "My Polygon");
var nullId = api.create("null", "My Null");
api.parent(primId, nullId);

unParent(layerId:string)

Move a layer up one level of hierarchy to its parent's parent. This is the equivalent of the Un-Parent context menu item available in the Scene Tree.

// Given basicShape#1 is the child of another Layer...
api.unParent("basicShape#1");

getParent(layerId:string) → string

Return the layerId of a Layer's parent.

var primId = api.primitive("polygon", "My Polygon");
var nullId = api.create("null", "My Null");
api.parent(primId, nullId);
console.log(api.getParent(primId));

getNiceName(layerId:string) → string

Return the 'nice name' of a Layer.

var nullId = api.create("null", "My Null");
console.log(api.getNiceName(nullId));

rename(layerId:string, name:string)

Rename a Layer.

/// rename all selected items
var sel = api.getSelection();
sel.forEach(function (item, index) {
api.rename(item, "My Name "+index);
});

offsetLayerTime(layerId:string, delta:int)

Offset a Layer's Visibility Clip and any related animation in time.

const layerId = api.primitive("rectangle", "Rectangle");
api.setOutFrame(layerId, 50);
api.offsetLayerTime(layerId, 100);

setStroke(layerId:string, isOn:bool)

Enable/disable the Stroke for a Shape.

var primId = api.primitive("rectangle", "Rectangle");
api.setFill(primId, false);
api.setStroke(primId, true);
api.set(primId, {"stroke.strokeColor": "#049dd9", "stroke.width": 20});

hasStroke(layerId:string) → bool

Returns true if a Shape has a Stroke.

var primId = api.primitive("rectangle", "Rectangle");
api.setStroke(primId, true);
console.log(api.hasStroke(primId));

setFill(layerId:string, isOn:bool)

Enable/disable the Fill for a Shape.

var primId = api.primitive("rectangle", "Rectangle");
api.setFill(primId, false);
api.setStroke(primId, true);

hasFill(layerId:string) → bool

Returns true if a Shape has a Fill.

var primId = api.primitive("rectangle", "Rectangle");
console.log(api.hasFill(primId));

getBoundingBox(layerId:string, worldSpace:bool) → {x:number, y:number, width:number, height:number, centre:{x:number, y:number}, left:number, right:number, top:number, bottom:number}

Return the bounding box of the specified Layer.

var primId = api.primitive("polygon", "My Polygon");
var bbox = api.getBoundingBox(primId, true);
console.log(JSON.stringify(bbox));

getSelectionBoundingBox() → {x:number, y:number, width:number, height:number, centre:{x:number, y:number}, left:number, right:number, top:number, bottom:number}

Return the world space bounding box of the selected Layers.

const shape1 = api.create("basicShape");
api.set(shape1, {"position": [450, -120]});
const shape2 = api.create("basicShape");
api.set(shape2, {"position": [-100, 210]});
api.select([shape1, shape2]);
let bbox = api.getSelectionBoundingBox();
console.log(JSON.stringify(bbox));

isVisible(layerId:string, includeHierarchy:bool) → bool

Determines if a Layer is visible in the Viewport. This considers the Layer's 'Hidden' attribute and the state of the Layer's Visibility Clip at the current frame. When the includeHierarchy argument is true, if the Layer is the child of another Layer, the visibility of that Layer (and its parent's and so on) is also considered.

const layerId = api.create("basicShape");
console.log(api.isVisible(layerId, false));

getInFrame(layerId:string) → int

Return the first frame of a visibility clip.

var sel = api.getSelection();
for (let layerId of sel) {
console.log(api.getInFrame(layerId));
}

setInFrame(layerId:string, frame:int)

Set the first frame of a visibility clip.

const layerId = api.primitive("rectangle", "Rectangle");
api.setInFrame(layerId, 50);

getOutFrame(layerId:string) → int

Return the last frame of a visibility clip.

var sel = api.getSelection();
for (let layerId of sel) {
console.log(api.getOutFrame(layerId));
}

setOutFrame(layerId:string, frame:int)

Set the last frame of a visibility clip.

const layerId = api.primitive("rectangle", "Rectangle");
api.setOutFrame(layerId, 50);

getInOutKeyframeIds(layerId:string) → [object]

Returns an array of objects containing the keyframe ids of all the in and out points for a Layer.

const layer1 = api.create("basicShape");
const keyIds = api.getInOutKeyframeIds(layer1);
console.log(JSON.stringify(keyIds));

getActiveCamera() → string

Return the active Camera's Layer Id. A Camera is considered 'active' when its visibility is on at the current frame. Where more than one Camera is visible on the same frame, the Camera highest in the hierarchy is considered the active one.

api.create("planarCamera");
console.log(api.getActiveCamera());

hasActiveCamera() → bool

Return whether there is an active Camera in the active Composition. A Camera is considered 'active' when its visibility is on at the current frame.

console.log(api.hasActiveCamera());

bringForward()

Move the selected layers up the layer stack by one place.

const shape1 = api.create("basicShape", "Shape1");
const shape2 = api.create("basicShape", "Shape2");
api.select([shape1]);
api.bringForward();

bringToFront()

Move the selected layers to the top of the layer stack.

const shape1 = api.create("basicShape", "Shape1");
const shape2 = api.create("basicShape", "Shape2");
const shape3 = api.create("basicShape", "Shape3");
api.select([shape1]);
api.bringToFront();

moveBackward()

Move the selected layers down the layer stack by one place.

const shape1 = api.create("basicShape", "Shape1");
const shape2 = api.create("basicShape", "Shape2");
api.select([shape2]);
api.moveBackward();

moveToBack()

Move the selected layers to the bottom of the layer stack.

const shape1 = api.create("basicShape", "Shape1");
const shape2 = api.create("basicShape", "Shape2");
const shape3 = api.create("basicShape", "Shape3");
api.select([shape3]);
api.moveToBack();

isShape(layerId:string) → bool

Checks if a specified Layer is a Shape.

const shape1 = api.primitive("ellipse", "Ellipse");
console.log(api.isShape(shape1));

Working with Attributes

getSelectedAttributes() → [object]

This will return an array containing the paths of the selected attributes.

var selAttr = api.getSelectedAttributes();
for (const [layerId, attr] of selAttr) {
console.log(layerId+"."+attr);
}

set(layerId:string, arguments:object)

Set values for a Layer's attributes.

// Create a Rectangle and set its Size, Position, Rotation and Fill Color
var primId = api.primitive("rectangle", "My Rectangle");
api.set(primId, {"generator.dimensions":[100,370], "position": [100, 200], "rotation": 50, "material.materialColor": "#8dc429"});
// Create a Text Shape and set its Font Family and Style
var textId = api.create("textShape", "My Text");
api.set(textId, {"font":{"font":"Arial", "style":"Bold"}});
// Collapse the hierarchy of a layer
api.set("basicShape#1", {"hierarchy": false});

get(layerId:string, attrId:string) → Object/Value

Get the values for a Layer's attributes.

var primId = api.primitive("rectangle", "My Rectangle");
api.set(primId, {"material.materialColor": "#8dc429", "generator.dimensions":[100,370], "rotation": 50, "position": [100, 200]});
var obj = api.get(primId, "position");
console.log(JSON.stringify(obj))

setGenerator(layerId:string, attrId:string, type:string)

Some layers in Cavalry contain Generators, these are discrete feature blocks that are used to extend the functionality of layers. For example the Basic Shape layer has a Generator to determine the shape it creates (e.g Ellipse, Rectangle...). Generators can be set with this function.

// Create an Ellipse and set it up
var ellipseId = api.primitive("ellipse", "Ellipse");
api.set(ellipseId, {"generator.radius.x": 10, "generator.radius.y": 10, "hidden": true});
// Create a Duplicator
var duplicatorId = api.create("duplicator", "Duplicator");
// Connect the Ellipse to the Duplicator
api.connect(ellipseId, "id", duplicatorId, "shapes");
// Change the Distribution on the Duplicator to a Custom Distribution
api.setGenerator(duplicatorId, "generator", "circleDistribution");
// Set the Distribution count
api.set(duplicatorId, {"generator.count": 10});

getGenerators(layerId:string) → string[]

Some layers in Cavalry contain Generators, these are discrete feature blocks that are used to extend the functionality of layers. For example the Basic Shape layer has a Generator to determine the shape it creates (e.g Ellipse, Rectangle). Generators on a layer can be listed with this command.

var layerId = api.create("connectShape", "Connect Shape");
var generatorId = api.getGenerators(layerId);
for (gId of generatorId) {
console.log(gId);
}

getCurrentGeneratorType(layerId:string, attrId:string) → string

Returns the current Generator type (which can be used with setGenerator).

var ellipseId = api.primitive("ellipse", "My Ellipse")
console.log(api.getCurrentGeneratorType(ellipseId, "generator"))

setAttributeExpression(layerId:string, attrId:string, expression:string)

Set an attribute expression, this will take whatever the input value is in the expression, and manipulate it in some way (multiply, add to it etc.).

var rectId = api.primitive("rectangle", "My Rectangle");
api.set(rectId, {"position.x": 300});

var starId = api.primitive("star", "Star");
api.set(starId, {"position.x": -300});

// Connect the result of the Star to the Rectangle
api.connect(starId, "position.y", rectId, "position.y");

// Add an attribute expression
api.setAttributeExpression(rectId, "position.y", "*2");
//api.setAttributeExpression(rectId, "position.y", "%50");
//api.setAttributeExpression(rectId, "position.y", "clamp(-45, value, 45)");
//api.setAttributeExpression(rectId, "position.y", "sqrt(value)");

// Power the stars movement with an Oscillator
var oscillatorId = api.create("oscillator", "Oscillator");
api.set(oscillatorId, {"strength": 1500});

api.connect(oscillatorId, "id", starId, "position.y");
api.play();

connect(fromLayerId:string, fromAttrId:string, toLayerId:string, toAttrId:string)

Connect one attribute to another. The result or output of a Layer is referred to as the id connection.

var textId = api.create("textShape", "Text");
var pathfinderId = api.create("pathfinder", "Pathfinder");
var starId = api.primitive("star", "Star");
api.set(starId, {"generator.radius": 300});
// Connect the result of the Star to the Pathfinder
api.connect(starId, "id", pathfinderId, "inputShape");
// Connect the result of the Pathfinder to the Text.Position
api.connect(pathfinderId, "id", textId, "position");

disconnect(fromLayerId:string, fromAttrId:string, toLayerId:string, toAttrId:string)

Remove connections between attributes.

var primId = api.primitive("rectangle", "Rectangle");
var oscillatorId = api.create("oscillator", "Oscillator");
api.connect(oscillatorId, "id", primId, "rotation");
console.log(api.getInConnection(primId, "rotation"));
api.disconnect(oscillatorId, "id", primId, "rotation");
console.log(api.getInConnection(primId, "rotation"));

disconnectInput(layerId:string, attrId:string)

Disconnect an Attribute's input connection.

api.disconnectInput("basicShape#1", "position.x");

disconnectOutputs(layerId:string, attrId:string)

Disconnect all the output connections from an Attribute.

api.disconnectOutputs("basicShape#1", "position.x");

getInConnection(layerId:string, attrId:string) → string

Returns the input connection to an Attribute. An empty string is returned if there's no input on the Attribute in question.

var primId = api.primitive("rectangle", "Rectangle");
var oscillatorId = api.create("oscillator", "Oscillator");
api.connect(oscillatorId, "id", primId, "rotation");
console.log(api.getInConnection(primId, "rotation"));

getOutConnections(layerId:string, attrId:string) → string[]

Returns all the output connections from an Attribute.

var primId = api.primitive("rectangle", "Rectangle");
var oscillatorId = api.create("oscillator", "Oscillator");
api.connect(oscillatorId, "id", primId, "rotation");
console.log(api.getOutConnections(oscillatorId, "id"));

getSelectedKeyframes() → object

This returns the selected keyframes as an enumerable string-keyed object. Each string is an attribute path, and each key is an array of frame numbers on which a keyframe resides.

let selKeys = api.getSelectedKeyframes();
for (const [key, value] of Object.entries(selKeys)) {
console.log(key+": "+value);
}

keyframe(layerId:string, frame:int, {object}) → string

Set keyframes for Layers and return the keyframeId.

const primId = api.primitive("rectangle", "My Rectangle");
const kfId1 = api.keyframe(primId, 0, {"scale.x": 5.});
console.log(kfId1);
const kfId2 = api.keyframe(primId, 100, {"scale.x": 1.});
console.log(kfId2);

deleteKeyframe(layerId:string, attrId:string, frame:int)

Remove a Layer's keyframes.

var primId = api.primitive("rectangle", "My Rectangle");
api.keyframe(primId, 0, {"scale.x": 5.});
api.keyframe(primId, 50, {"scale.x": 7.});
api.keyframe(primId, 100, {"scale.x": 1.});
api.deleteKeyframe(primId, "scale.x", 50);

modifyKeyframe(layerId:string, data:object)

Modify the keyframe time (frame number) or value. The supplied object must include a frame key, in addition to this it can also include:

  • newFrame // Specify a new frame for the keyframe (optional).
  • newValue // Specify a new value for the keyframe (optional).
  • type // The keyframe type as an integer 0 Bezier, 1 Linear, 2 Step (optional).

Example of modifying keyframe values and frames:

var primId = api.primitive("rectangle", "My Rectangle");
api.keyframe(primId, 0, {"scale.x": 5.});
api.keyframe(primId, 100, {"scale.x": 1.});
api.modifyKeyframe(primId, {"scale.x": {"frame": 0, "newValue": 3.5, "newFrame": 10}});

Example of setting all keyframes to step interpolation.

let ellipseId = api.primitive("ellipse", "Ellipse");
// Create some values to set as keyframes
let keyValues = [-200,200,-300, 300]
let keyTime = 0;
// Set some keyframes for us to modify
for (let value of keyValues) {
api.keyframe(ellipseId, keyTime, {"position.x":value});
keyTime+=40;
}

// Get the keyframe times
let times = api.getKeyframeTimes("basicShape#1","position.x")
for (let frame of times) {
// Set the keyframes to step interpolation
api.modifyKeyframe(ellipseId, {"position.x":{"frame": frame, "type":2}});
}

modifyKeyframeTangent(layerId:string, data:object)

Modify the keyframe tangents. The supplied object must include a frame key. Both the in and out handle will be affected unless a handle is specified and the handle is not weight and angle locked.

  • inHandle // An optional boolean value used to specify the inHandle to be affected.
  • outHandle // An optional boolean value used to specify the outHandle to be affected.
  • angleLocked // Boolean stating if the key tangents are angle locked or not (optional).
  • weightLocked // Boolean stating if the key tangents are weight locked or not (optional).
  • angle // Set a new angle for the keyframe tangent, 0 is flat (optional).
  • weight // Set a new weight for the keyframe tangent (optional).

Example setting flat keyframes:

// Make a new ellipse
let ellipseId = api.primitive("ellipse", "Ellipse");
// Create some values to set as keyframes
let keyValues = [-200,200,-300, 300]
let keyTime = 0;
// Set some keyframes for us to modify
for (let value of keyValues) {
api.keyframe(ellipseId, keyTime, {"position.x":value});
keyTime+=40;
}

// Get the keyframe times
let times = api.getKeyframeTimes(ellipseId,"position.x")
for (let frame of times) {
// Modify the tangents, giving them all a weight of 20 and an angle of 0 (flat)
api.modifyKeyframeTangent(ellipseId, {"position.x":{"angle":0, "frame": frame, "weight":20}});
}

Example breaking tangents and weighting the outHandles.

// Create a Shape
let ellipseId = api.primitive("ellipse", "Ellipse");
// Store some values to set as keyframes
let keyValues = [-200,200,-300, 300]
let keyTime = 0;
// Set some keyframes to modify
for (let value of keyValues) {
api.keyframe(ellipseId, keyTime, {"position.x":value});
keyTime+=40;
}

// Get the keyframe times
let times = api.getKeyframeTimes(ellipseId, "position.x")
for (let frame of times) {
// Set the handle weights to 0
api.modifyKeyframeTangent(ellipseId, {"position.x":{"frame": frame, "weight":0}});
// Set the weight for only the out handles
api.modifyKeyframeTangent(ellipseId, {"position.x":{"frame": frame, "weight":20, "outHandle": true, "weightLocked": false}});
}

getKeyframeIdsForAttribute (layerId:string, attrId:string)

Get all keyframeIds for a particular layerId's attribute. This can be used in combination with setUserData.

var primId = api.primitive("rectangle", "My Rectangle");
api.keyframe(primId, 0, {"position.x": 10});
console.log(api.getKeyframeIdsForAttribute(primId, "position.x"));

getSelectedKeyframeIds() → [string]

Get the ids for selected keyframes. This can be used in combination with setUserData.

// Create a Shape, add some keyframes and then select them before running: 
console.log(api.getSelectedKeyframeIds());

setSelectedKeyframeIds(keyframeIds:[string])

Set the keyframe selection. To clear the keyframe selection send an empty array through.

// Create a Shape and add some keyframes to scale.x
var primId = api.primitive("rectangle", "My Rectangle");
api.keyframe(primId, 0, {"scale.x": 4.});
api.keyframe(primId, 50, {"scale.x": 3.});
api.keyframe(primId, 100, {"scale.x": 1.});

//console.log(api.getKeyframeIdsForAttribute(primId, "scale.x"));

// Select the first and third keyframes (uncomment above to return keyframeIds)
api.setSelectedKeyframeIds(["keyframe#3", "keyframe#5"]);

getAttributeFromKeyframeId(keyframeId:string) → string

Get the attribute path for a given keyframe.

const keyIds = api.getSelectedKeyframeIds();

for (const keyId of keyIds) {
console.log(api.getAttributeFromKeyframeId(keyId))
}

magicEasing(layerId:string, attrId:string, frame:int, easingName:string)

Apply Magic Easing to a new or existing keyframe.

api.magicEasing("basicShape#1", "position.x", 25, "SlowIn");

Valid Magic Easing names are:

  • "SlowIn"
  • "SlowOut"
  • "SlowInSlowOut"
  • "VerySlowIn"
  • "VerySlowOut"
  • "VerySlowInVerySlowOut"
  • "SpringIn"
  • "SpringOut"
  • "SpringInSpringOut"
  • "SmallSpringIn"
  • "SmallSpringOut"
  • "SmallSpringInSmallSpringOut"
  • "AnticipateIn"
  • "OvershootOut"
  • "AnticipateInOvershootOut"
  • "BounceIn"
  • "BounceOut"
  • "BounceInBounceOut"

getKeyframeTimes(layerId:string, attrId:string)

deleteAnimation(layerId:string, attrId:string)

Delete all keyframes on an attribute.

getAttrType(layerId:string, attrId:string) → string

Get the data type of the Attribute

var layerId = api.create("javaScript", "JS Layer");
api.addDynamic(layerId, "array", "string");
console.log(api.getAttrType(layerId, "array.1"));

resetAttribute(layerId:string, attrId:string)

Reset an Attribute back to its default value.

addArrayIndex(layerId:string, attrId:string) → int

Add a new child to an array Attribute.

var arrayId = api.create("valueArray", "My Value Array");
api.addArrayIndex(arrayId, "array")
api.addArrayIndex(arrayId, "array")
api.set(arrayId, {"array.0": 10, "array.1": 20, "array.2": 30});

removeArrayIndex(layerId:string, attrId:string)

Remove an Attribute from an array

getArrayCount(layerId:string, attrId:string) → int

Return the number of Attributes in the array

var arrayId = api.create("valueArray", "My Value Array");
api.addArrayIndex(arrayId, "array")
api.addArrayIndex(arrayId, "array")
console.log(api.getArrayCount(arrayId, "array"));

addDynamic(layerId:string, attrId:string, type:string)

Add a dynamic attribute to a layer. Dynamic attributes are a special kind of Array Attribute in that they can be of different types. Only certain special layers can have dynamic attributes added to them, for example the JavaScript Utility. Once added, these attributes can be renamed by using renameAttribute or removed by using removeArrayIndex. The name of the attribute is used in the JavaScript execution, and in the UI, but getting and setting these attributes is done by index (e.g. array.0) and not by the Attribute name.

var layerId = api.create("javaScript", "JS Layer");
api.addDynamic(layerId, "array", "double");
api.addDynamic(layerId, "array", "bool");
api.addDynamic(layerId, "array", "string");
api.addDynamic(layerId, "array", "int2");
api.addDynamic(layerId, "array", "double2");
api.addDynamic(layerId, "array", "color");

/// an example of setting and getting a Dynamic Attribute
var layerId = api.create("javaScript", "JS Layer");
api.addDynamic(layerId, "array", "double");
api.set(layerId, {"array.1": 10});
var value = api.get(layerId, "array.1");
console.log(value);

getCustomAttributeName(layerId:string, attrId:string) → string

Return the nice name for Dynamic or Array Attributes.

var arrayId = api.create("valueArray", "My Value Array");
api.renameAttribute(arrayId, "array.0", "Example Name");
console.log(api.getCustomAttributeName(arrayId, "array.0"));

getAttrParent(layerId:string, attrId:string) → string

Return the Id of the parent attribute. If there is no parent attribute, an empty string is returned.

let shapeId = api.create("basicShape");
let parentAttrId = api.getAttrParent(shapeId, "position.x");
console.log(parentAttrId);

getAttrChildren(layerId:string, attrId:string) → [string]

Return the Ids of any child attributes. If there are no child attributes, an empty string is returned.

var arrayId = api.create("valueArray", "My Value Array");
api.addArrayIndex(arrayId, "array")
api.addArrayIndex(arrayId, "array")
let children = api.getAttrChildren(arrayId, "array");
console.log(children);

renameAttribute(layerId:string, attrId:string, newName:string)

Rename Dynamic or Array Attributes. Array Attributes can be found on the Array Utilities such as the Color Array. Dynamic Attributes can be found on Layers like the JavaScript Utility. A type must be chosen for Dynamic Attributes when adding them.

var arrayId = api.create("valueArray", "My Value Array");
api.renameAttribute(arrayId, "array.0", "Example Name");

getOutConnectedAttributes(layerId:string) → string[]

List the output connections from a Layer.

const layer = api.primitive("ellipse", "Ellipse");
api.connect(layer, "scale.x", layer, "scale.y");
api.connect(layer, "position.x", layer, "position.y");
const outConn = api.getOutConnectedAttributes(layer);
console.log(outConn);

getInConnectedAttributes(layerId:string) → string[]

List the input connections to a Layer.

const layer = api.primitive("ellipse", "Ellipse");
api.connect(layer, "scale.x", layer, "scale.y");
api.connect(layer, "position.x", layer, "position.y");
const inConn = api.getInConnectedAttributes(layer);
console.log(inConn);

getAttributes(layerId:string) → string[]

Return a list of all the attributes that exist on a Layer.

var layerId = api.create("null", "My Null");
var attrIds = api.getAttributes(layerId);
for (aId of attrIds) {
console.log(aId);
}

hasAttribute(layerId:string, attrId:string) → bool

Check to find out if a particular attribute exists on a Layer.

const layer = api.primitive("ellipse", "Ellipse")
const attr = api.hasAttribute(layer, "position.x");
console.log(attr);

getAnimatedAttributes(layerId:string) → string[]

Return a list of all the animated attributes that exist on a Layer.

var layerId = api.create("null", "My Null");
api.keyframe(layerId, 0, {"scale.x": 5.});
api.keyframe(layerId, 100, {"scale.x": 1.});
var attrIds = api.getAnimatedAttributes(layerId);
for (aId of attrIds) {
console.log(aId);
}

isAnimatedAttribute(layerId:string, attrId:string) → bool

Check to find out if a particular attribute on a Layer is animated.

var layerId = api.create("null", "My Null");
api.keyframe(layerId, 0, {"scale.x": 5.});
api.keyframe(layerId, 100, {"scale.x": 1.});
console.log(api.isAnimatedAttribute(layerId, "scale.x"));

graphPreset(layerId:string, attrId:string, presetIndex:int)

Sets a preset for a Graph Attribute. The preset index can be: 0: s-curve 1: ramp 2: linear 3: flat

flipGraph(layerId:string, attrId:string, direction:string)

Flips the points on a Graph Attribute - valid direction arguments are "horizontal" and "vertical".

let staggerId = api.create("stagger", prefix+"Stagger");
api.flipGraph(staggerId, "graph", "vertical");

addToControlCentre(layerId:string, attrId:string)

Add an attribute to the Control Centre.

var shapeId = api.create("null", "My Null");
api.addToControlCentre(shapeId, "position.x");

removeFromControlCentre(layerId:string, attrId:string)

Remove an attribute from the Control Centre.

//Create a Null and add position.x to Control Centre
var shapeId = api.create("null", "My Null");
api.addToControlCentre(shapeId, "position.x");
//Then, assuming the null's LayerId is null#1, remove position.x from the Control Centre
api.removeFromControlCentre("null#1", "position.x");

addPreCompOverride(layerId:string, attrId:string)

Add a Pre-Comp Override to an Attribute.

let layerId = api.primitive("ellipse", "My Ellipse");
api.select([layerId]);
let preCompId = api.preCompose();
api.addPreCompOverride(layerId, "generator.radius");

removePreCompOverride(layerId:string, attrId:string)

Remove a Pre-Comp Override to an Attribute.

let layerId = api.primitive("ellipse", "My Ellipse");
api.select([layerId]);
let preCompId = api.preCompose();
api.addPreCompOverride(layerId, "generator.radius");
let numOfOverrides = api.listPreCompOverrides(preCompId).length
// Remove Pre-Comp Override
api.removePreCompOverride(layerId, "generator.radius");
let numOfOverridesAfterRemove = api.listPreCompOverrides(preCompId).length

console.log("Overrides after add: " + numOfOverrides + ". Overrides after remove: " + numOfOverridesAfterRemove)

listPreCompOverrides(preCompId:string) → object

Return a list of key:value pairs where the key is the Attribute being overridden and the value is the attribute stored on the Pre-Comp.

let layerId = api.primitive("ellipse", "My Ellipse");
api.select([layerId]);
let preCompId = api.preCompose();
api.addPreCompOverride(layerId, "generator.radius");
api.addPreCompOverride(layerId, "position");
console.log(JSON.stringify(api.listPreCompOverrides("compositionReference#1")));

Shapes

centrePivot(layerId:string, centroid:bool)

Centre the Pivot of the specified layer. If Centroid is true the Pivot will be moved to the centre of mass.

freezeTransform(layerId:string)

Freeze the transform (position, rotation, scale, pivot, skew) of the specified layer. This can be used to make a Shape's current position its zero position.

resetTransform(layerId:string)

Reset a Shape's transform back to the default state (this will also clear any frozen transformations).

getDrawInstructionsForSelection() → string

Copy the selected Shape(s) as code. The resulting code can be pasted into a new tab and run to create a new Editable Shape based on those copied.

var path = api.getDrawInstructionsForSelection();
console.log(path);

move(x:number,y:number)

Move the selected Shapes.

let layer1Id = api.primitive("ellipse", "Ellipse1");
let layer2Id = api.primitive("ellipse", "Ellipse2");
api.set(layer2Id, {"position": [200, 0]});
api.select([layer1Id, layer2Id]);
api.move(100,100);

Editable Shapes

makeEditable(layerId:string, makeACopy:bool) → string

This will convert the selected Shape into an Editable Shape, which can then be edited with the getEditablePath and setEditablePath functions. If makeACopy is set to false the original layer will be deleted.

var primId = api.primitive("ellipse", "Ellipse");
var editableId = api.makeEditable(primId, false);

getEditablePath(layerId:string, worldSpace:bool) → object[]

This function returns an Editable Path object which can be edited and then set back to any Editable Shape layer.

Editable Paths and ordinary Paths (like the one in the Cavalry Module) are distinct. The worldSpace argument can be used to determine if path point coordinates are returned in local – unaware of the Editable Shape's position, rotation and scale – or world space where those transformations are applied. An Editable Path's points have in handles and out handles just like the points that are edited in the Viewport. They also have weight and angle locking settings. Ordinary Paths are constructed using moveTo, lineTo and cubicTo. In an Editable Path, an extra point can be added to the Contour's points array.

The schema is as follows:

[
{
"points": [
{
"position": {
"x": 0.0,
"y": 0.0
},
"outHandle": {
"x": 0.0,
"y": 0.0,
"selected": bool
},
"inHandle": {
"x": 0.0,
"y": 0.0,
"selected": bool
},
"weightLocked": bool,
"angleLocked": bool,
"selected": bool
}
],
"isClosed": bool
}
]

The inHandle and outHandle objects are optional (when they are missing a linear point will be created). Values marked bool need to be true or false.

var multiplier = 1.5;
var primId = api.primitive("ellipse", "Ellipse");
var editableId = api.makeEditable(primId, false);

let path = api.getEditablePath(editableId, false);
for (let contour of path) {
for (let point of contour.points) {
point.inHandle.x *= multiplier;
point.outHandle.x *= multiplier;
point.inHandle.y *= multiplier;
point.outHandle.y *= multiplier;
}
}
api.setEditablePath(editableId, true, path);

setEditablePath(layerId:string, worldSpace:bool, pathObject:object)

This will set the Editable Path on an Editable Shape (Primitives are not supported). See getEditablePath for details on the Editable Path schema. The worldSpace argument will determine if path point coordinates are set in local space – unaware of the Editable Shape's position, rotation and scale – or world space where those transformations are applied. If the Editable Path is accessed in world space, it should also be set in world space.

This example will flatten the selected bezier points to 0 on the Y axis.

var sel = api.getSelection();

for (let layerId of sel) {
if (api.getLayerType(layerId) != "editableShape") {
continue;
}
let path = api.getEditablePath(layerId, true);
for (let contour of path) {
for (let point of contour.points) {
if (!point.selected) {
continue;
}
point.position.y = 0;
point.inHandle.y = 0;
point.outHandle.y = 0;
}
}
api.setEditablePath(layerId, true, path);
}

makeFirstPoint(layerId:string)

This will make the selected point the first point in an Editable Path. This is like running the command in the Shape menu.

movePoint(x:number, y:number, localSpace:bool)

Move selected points by given X and Y values.

//select some editable points
api.movePoint(0,50, true);

setPointPosition(positionObject:object, localSpace:bool, handles:bool)

Move selected points to given X and Y positions. Values in the positionObject are optional, i.e {"x":20} and {"x":20, "y":50} are both valid. Use the handles argument to specify if in/out handles should be moved instead of the point. To move both, run the command twice with handles set to true and false.

//select some editable points
api.setPointPosition({"y":-50}, false, false)
api.setPointPosition({"y":-50}, false, true)

Rendering

render(renderQueueItemId:string)

Render a specific Render Queue Item.

const itemId = api.addRenderQueueItem(api.getActiveComp());
api.render(itemId);

renderAll()

Render all Render Queue Items in the Render Manager.

api.addRenderQueueItem(api.getActiveComp());
api.renderAll();

renderPNGFrame(filePath:string, scalePercent:number)

Render the current frame out as a PNG with a given scale set by scalePercent. Rendering in this way will only render the visible layers (i.e any soloed layers). The render extension (.png) will be added to the filename.

// This example will export each selected layer individually as a PNG at 100% and 200% scale.
// Get the selection
let sel = api.getSelection();
for (let layerId of sel) {
// Solo each layer
api.soloLayers([layerId]);
// Build a file path to export the render to
let filePath = api.getRenderPath() + "/" + api.getNiceName(layerId);
// Render out the image at 100% scale.
api.renderPNGFrame(filePath, 100);
// Add @2x to the file path
filePath += "@2x";
// Render out at 200% scale.
api.renderPNGFrame(filePath, 200);
// Log where we put the file
console.log("Rendered layer: "+layerId+" to: "+filePath+".png");
}
// Clear soloing
api.soloLayers([]);

renderSVGFrame(filePath:string, scalePercent:number, skipComps:bool)

Render the current frame out as an SVG with a given scale set by scalePercent. Setting skipComps to true will mean that Composition backgrounds do not get exported. Rendering in this way will only render the visible layers (i.e any soloed layers). The render extension (.svg) will be added to the filename.

getRenderQueueItems() → string[]

Return a list of the Render Queue Items in the Render Manager.

api.addRenderQueueItem(api.getActiveComp());
api.addRenderQueueItem(api.getActiveComp());
var items = api.getRenderQueueItems();
for (var item of items) {
console.log(item);
}

addRenderQueueItem(compId:string) → string

Add a new Render Queue Item to the Render Manager.

var itemId = api.addRenderQueueItem(api.getActiveComp());
console.log(itemId);

connectDynamicIndex(layerId:string, attrId:string)

Connect the Render Manager's Dynamic Index to another attribute.

var spreadsheetId = api.create("spreadsheet", "My Spreadsheet");
api.connectDynamicIndex(spreadsheetId, "rowIndex");
api.set(spreadsheetId, {"useFixedRow": true});

getDynamicIndex() → int

Return the current Dynamic Index.

console.log(api.getDynamicIndex());

backgroundRender(renderQueueItemId:string)

Render a specific Render Queue Item in the background. Note - there are no notifications for the render's status.

backgroundRenderAll()

Render all (active) Render Queue Items in the background. Note - there are no notifications for the render's status.

Working with the Scene

openScene(filePath:string, force:bool)

Open a Scene at the given location, this may present a Save Changes dialog unless force is set to true.

saveSceneAs(filePath:string) → string

Save the current Scene to a new location and return a boolean to confirm if it was successful.

saveScene() → string

Save the current Scene file and return a boolean to confirm if it was successful. If the current Scene has not yet been saved, a dialog will be presented asking where to save the scene.

sceneHasUnsavedChanges() → string

Return true if there have been any changes made to the Scene since the last save.

console.log(api.sceneHasUnsavedChanges());

importScene(path:string)

Import a Cavalry Scene (.cv) or Component (.cvc). A .cv file will be added to the Assets Window, a .cvc file will be added to the active Composition.

api.importScene("path/to/scene.cv");

loadAsset(path:string, isSequence:bool) → string

Load an asset with the given path. Set isSequence to true to attempt to load an image sequence from the file path.

// Load an image
api.loadAsset("/Path/To/image.png", false);

// Load an image sequence
api.loadAsset("/Path/To/sequence.00000.png", true);

reloadAsset(assetId:string)

Reloads an Asset with the given assetId

// First load an Asset, then run:
api.reloadAsset("asset#2");

replaceAsset(assetId:string, string:newPath)

Replace a file asset (e.g an image or CSV file).

jsonFromAsset(assetId:string) → object

Given a Text Asset (which is a .json file) or a Spreadsheet Asset, get the JSON object that Asset represents.

For the following example, first select a Text Asset (a JSON file imported into Cavalry).

let sel = api.getSelection();
if (sel.length) {
if (api.getLayerType(sel[0]) == "asset") {
let data = api.jsonFromAsset(sel[0]);
console.log(Object.keys(data).length);
}
}

It's also possible to use jsonFromAsset to query a .csv asset. CSV Assets will contain three members. rows will provide access to the row data. min will provide access to the minimum value of the column (if there is one), and max will give the maximum value.

// Return the first entry in the 'Text' column of a .csv asset (asset#2)
const csv = api.jsonFromAsset("asset#2");
const text = csv["Text"].rows[0]
console.log(text);

textFromAsset(assetId:string) → string

Given a Text Asset, get the raw string that Asset represents.

loadGoogleSheet(spreadsheetId:string, sheetId:string) → string

Load a Google Sheet Asset. If the sheetId argument is left blank (e.g. "") then the first sheet will be loaded. This function returns the newly created assetId.

info

The spreadsheetId and sheetId can be extracted from a Google Sheet's URL:

https://docs.google.com/spreadsheets/d/[spreadsheetId]/edit#gid=[sheetId]

replaceGoogleSheet(assetId:string, spreadsheetId:string, sheetId:string)

Replace an existing Google Sheet Asset with another. If the sheetId argument is left blank (e.g. "") then the first sheet will be loaded.

setProject(path:string)

Set the location of projectDescription.json in order to use relative filepaths.

api.setProject(path/to/project/);

clearProject()

Clear the Project.

api.clearProject();

getActiveComp() → string

Returns the currently active (open) Composition's id. This can be used to set Composition settings such as Resolution and Frame Range.

console.log(api.getActiveComp());

getComps() → string[]

Returns an array containing all the Compositions in the Scene.

let newCompId = api.createComp("Shiny New Comp");
let allComps = api.getComps();
for (let compId of allComps) {
console.log(compId);
}

createComp(niceName:string) → string

Create a new composition and return its id.

api.createComp("Shiny New Comp");

setActiveComp(compId:string)

Set the currently active composition.

let newCompId = api.createComp("Shiny New Comp");
api.setActiveComp(newCompId);

preCompose(name:string) → string

Create a new Composition containing the current selection and then reference that new Composition into the active Composition. The optional name argument specifies the name of the Layer in the Scene Window. The layerId of the newly created Composition Reference is returned. To return the compId of the Composition itself, use getCompFromReference.

// Create two Shapes.
var shape1 = api.primitive("superEllipse", "Super Ellipse");
var shape2 = api.primitive("rectangle", "Rectangle");
// Select them both.
api.select([shape1, shape2]);
// Add the selection to a Pre-Comp (Composition Reference) called 'New Pre-Comp'.
api.preCompose("New Pre-Comp");

getCompFromReference(layerId:string) → string

Given the layerId of a Composition Reference, get the compId of the Composition it references from the Assets Window.

// Create a Shape and select it.
var shape1 = api.primitive("superEllipse", "Super Ellipse");
api.select([shape1]);
// Add the selection to a Pre-Comp (Composition Reference) called 'New Pre-Comp'.
var preCompId = api.preCompose("New Pre-Comp");
// Rename the Composition in the Assets Window to match.
var compId = api.getCompFromReference(preCompId);
api.set(compId, {"niceName": "New Pre-Comp"});

createCompReference(compId:string) → string

Create a Composition Reference from an existing Composition and add it to the active Composition.

let newCompId = api.createComp("Shiny New Comp");
api.createCompReference(newCompId);

getAssetWindowLayers(topLevel:bool) → string[]

This will return an array of all the layers in the Asset Window. If topLevel is true then only the top level layers will be returned.

let assetLayerIds = api.getAssetWindowLayers(false);
for (let alId of assetLayerIds) {
console.log(alId);
}

getAssetType(assetId:string) → string

Return the asset type (i.e image, audio, spreadsheet, movie, svg).

// First load an Asset, then run:
console.log(api.getAssetType("asset#2"));

getAssetFilePath(assetId:string) → string

Get the file path of a file asset such as a spreadsheet, image, or font.

var assets = api.getAssetWindowLayers(false);
for (let assetId of assets) {
let type = api.getAssetType(assetId);
if (type != "unknown") {
let filePath = api.getAssetFilePath(assetId);
console.log("Checking: "+filePath+" of type "+type+" exists: "+api.filePathExists(filePath));
}
}

createAssetGroup(niceName:string) → string

Create a Group in the Assets Window, this will return the layerId of the new Group.

api.createAssetGroup("My Asset Group");

soloLayers(layerIds:string[])

This will solo the layer ids supplied in the array argument.

// This example will export each selected layer individually as a PNG at 100% and 200% scale.
// Get the selection
let sel = api.getSelection();
for (let layerId of sel) {
// Solo each layer
api.soloLayers([layerId]);
// Build a file path to export the render to
let filePath = api.getRenderPath() + "/" + api.getNiceName(layerId);
// Render out the image at 100% scale.
api.renderPNGFrame(filePath, 100);
// Add @2x to the file path
filePath += "@2x";
// Render out at 200% scale.
api.renderPNGFrame(filePath, 200);
// Log where we put the file
console.log("Rendered layer: "+layerId+" to: "+filePath+".png");
}
// Clear soloing
api.soloLayers([]);

getAllSceneLayers() → [string]

Return a list of all Layers in a Scene.

let layers = api.getAllSceneLayers();
for (layer of layers) {
console.log(layer);
}

Files and Paths

Files and paths are absolute, use the methods in this section as the base path if to build paths relative to the Project. Using the ui.scriptLocation variable can be used to build paths relative to a Script.

load(filePath:string) → bool

This will load and run a JavaScript file making the functions contained within it available to use in the current script. This is not a module loader. Scripts loaded in this way are not placed into a namespace/ module and are free functions/objects.

// Contents of script.js
function helloWorld() {
console.log("Hello World.");
}
// Then in Cavalry
api.load("path/to/script.js");
helloWorld();

presentOpenFile(startPath:string, title:string, fileFilter:string) → string

Open and filter a dialogue window to load/import files. Returns the file name to be loaded or an empty string if the user cancels the dialogue. The fileFilter is a title, followed by parenthesis containing space separated file type descriptions. e.g. "Data File (*.json *.csv)", "Cavalry File (*.cv *.cvc)", "Palettes (*.pal)".

// Create a button
var button = new ui.Button("Import File");
// Set the onClick callback function
button.onClick = function () {
console.log(api.presentOpenFile(api.getProjectPath(), "Import JSON or CSV", "Data File (*.json *.csv)"));
}
// Add the button to the layout
ui.add(button);
// Show the window
ui.show();

presentSaveFile(startPath:string, title:string, fileFilter:string, defaultFileName:string) → string

Open a dialogue window to save files. Returns the saved file name or an empty string if the user cancels the dialogue. The fileFilter is a title, followed by parenthesis containing the file type description. e.g. "Save JSON", "JSON File (*.json)", "Config File.json". Multiple fileFilters can be added by separating the file types with a double semi-colon (;;) – a drop down menu will then appear within the dialogue for a user to choose a file type from.

// Simple example

// Create a button
var button = new ui.Button("Save File");
// Set the onClick callback function
button.onClick = function () {
console.log(api.presentSaveFile(api.getProjectPath(), "Save JSON", "JSON File (*.json)", "Config File.json"));
}
// Add the button to the layout
ui.add(button);
// Show the window
ui.show();
// Example including a filter for multiple file types

// Create a button
var button = new ui.Button("Save File");
// Set the onClick callback function
button.onClick = function () {
console.log(api.presentSaveFile(api.getProjectPath(), "Save Data", "JSON File (*.json);;CSV File (*.csv)", "Config File"));
}
// Add the button to the layout
ui.add(button);
// Show the window
ui.show()

exec(scriptId:string, scriptSource:string) → bool

Like load, exec will load and run JavaScript but it does not require a saved file.

The first argument is a scriptId. The scriptId exists because Cavalry asks for permission to perform certain tasks (like using the WebAPIs or writing to the hard drive) on a per-script basis. This information is stored using the scriptId so that permission will only be requested once per Id.

We recommend using reverse domain notation com.<yourCompany>.<yourScript> as a basis for scriptIds.

let myScript = "console.log('Hello World!')"
api.exec("com.scenegroup.scriptName", myScript)

getProjectPath() → string

Gets the Project path (if a Project is set). This path does not include a trailing /

console.log(api.getProjectPath());

getRenderPath() → string

Gets the Render path (if a Project is set). This path does not include a trailing /

console.log(api.getRenderPath());

getAssetPath() → string

Gets the Project Asset path (if a Project is set). This path does not include a trailing /

console.log(api.getAssetPath());

getScenesPath() → string

Gets the Scene's path (if a Project is set). This path does not include a trailing /

console.log(api.getScenesPath());

getSceneFilePath() → string

If the current Scene has been saved this will return its filepath, otherwise it will return an empty string.

console.log(api.getSceneFilePath());

getPreferencesPath() → string

Get the location of the Cavalry preferences folder.

console.log(api.getPreferencesPath());

filePathExists(path:string) → bool

Return whether a path to a file or folder exists.

console.log(api.filePathExists(api.getPreferencesPath()));

getFileNameFromPath(path:string, includeExtension:bool) → string

Return the file name from a path with an optional argument to include the file extension.

/// will return `file`
console.log(api.getFileNameFromPath("/path/to/file.png"));

/// will return `file.png`
console.log(api.getFileNameFromPath("/path/to/file.png", true));

getExtensionFromPath(path:string) → string

Return the extension of a file.

/// will return `.png`
console.log(api.getExtensionFromPath("/path/to/file.png"));

getFolderFromPath(path:string) → string

Return the containing folder of a file.

/// will return `/path/to`
console.log(api.getFolderFromPath("/path/to/file.png"));

makeFolder(path:string) → bool

Create a folder at the location given in path and return if the operation was a success.

// Update the example path below and uncomment the line
// const newFolder = api.makeFolder("/path/to/newFolder");
let message = "";
if (newFolder == true) {
message = "New folder created.";
}
else {
message = "New folder failed.";
}
console.log(message);

deleteFilePath(path:string) → bool

Delete a file from the file system and return true if successful. Typically this can be used for temporary files a user should never see.

danger

deleteFilePath instantly deletes files. They are not sent to the trash so there is no way to retrospectively recover them.

getAppAssetsPath() → string

Get the location of the App Assets (this is useful for accessing app icons for a script).

const icon = `${api.getAppAssetsPath()}/icons/transform@2x.png`;
const button = new ui.ImageButton(icon);
button.setSize(32, 32);
ui.add(button);
ui.show();

writeToFile(filePath:string, content:string, overwrite = false) → bool

Write a string to a file. Returns if the write was successful. If a file already exists, an error is returned. Set the 'overwrite' argument to true to override the error and replace the existing file - proceed with caution.

Binary data cannot be written from this API, for that, please use writeEncodedToBinaryFile. filePathExists can also be used to add any additional success/warning messages to the console.

Backslashes

JavaScript does not support single backslashes (\) in file paths (it is an escape character) but slashes (/) are valid on Windows. Alternatively, replace single backslashes with double backslashes (\\).

var primId = api.primitive("polygon", "My Polygon");
var obj = api.get(primId, "polyMesh");

var objContents = "#this exporter is incomplete and only works with QuickLook on macOS\n\n";
objContents += "o testShape\n\n";
var face = "f ";

var meshes = obj.meshes;
for (var mesh of meshes) {
var index = 1; // obj face index starts at 1
for (var pointData of mesh.path) {
// 0 = moveTo and 1 = lineTo
if (pointData.type == 0 || pointData.type == 1) {
objContents+="v "+pointData.points[0].x.toFixed(4)+" "+pointData.points[0].y.toFixed(4)+"\n";
face += index + " ";
}
++index;
}

}

objContents += "usemtl None\n"
objContents += "s off\n"
objContents += face;

// This line is commented out as you will need to provide a file path
//api.writeToFile("/some/folder/test.obj", objContents);

writeEncodedToBinaryFile(filePath:string, content:string) → bool

Write an encoded string to a file and returns if the write was successful. Caution - this will overwrite any existing file. Please note, only files encoded using encodeBinary will be properly decoded.

var encoded = api.encodeBinary("/some/folder/original.png");
api.writeEncodedToBinaryFile("/some/folder/copy.png", encoded);

readFromFile(filePath:string) → string

Read a file as a string.

// Please note this file path will need to point to a real file (e.g a .txt, .json, .csv, .obj etc., etc.)
var text = api.readFromFile("/some/folder/test.obj");
console.log(text);

encodeBinary(filePath:string) → string

Read a binary file (like an image), and encode it into base64. This can then be stored as a variable in JavaScript for use later (i.e with writeEncodedToBinaryFile).

// Please note this file path will need to point to a real file (e.g a .png, .jpg etc., etc.)
var encoded = api.encodeBinary("/some/folder/test.png");
api.writeToFile("/some/folder/encoderDump.txt", encoded);

convertSVGToLayers(filename:string) → [string]

Convert an SVG file to Layers without the need to import the SVG as an asset first.

const svg = api.convertSVGToLayers("/path/to/filename.svg");
console.log(svg);

Utilities

setClipboardText(stringToCopy:text)

Add a string to the clipboard.

api.setClipboardText("Copy test from Cavalry");

getClipboardText() → string

Return the contents of the clipboard as a string.

api.setClipboardText("Copy test from Cavalry");
console.log(api.getClipboardText());

runProcess(command:string, arguments:[string]) → object

This runs a system process and waits for the result which is returned as an object. GUI scripts that try to run this function will trigger a warning asking for users to trust the script. This is a blocking action, the UI will freeze until the process completes. For non blocking processes see runDetachedProcess below.

caution

Proceed with caution when running system processes to ensure scripts do not compromise system security.

// macOS example
var res = api.runProcess("sh", ["-c", "python3 --version"]);
if (res.error) {
console.log(res.error);
} else {
console.log(res.output);
}
// on Windows the command to run (first argument) would be "cmd.exe" or "path/to/powerShell.exe" and so forth.
var res = api.runProcess("cmd.exe", ["/c echo hello world"]);
if (res.error) {
console.log(res.error);
} else {
console.log(res.output);
}

runDetachedProcess(command:string, arguments:[string])

This runs a system process in a separate thread. GUI scripts that try to run this function will trigger a warning asking for users to trust the script. This is a non-blocking action.

caution

Proceed with caution when running system processes to ensure scripts do not compromise system security.

// macOS example
api.runDetachedProcess("sh", ["-c", "python3 --version"]);
// Windows example - the command to run (the first argument) would be e.g. "cmd.exe" or "path/to/powerShell.exe".
api.runDetachedProcess("cmd.exe", ["/c echo hello world"]);

openURL(url:string)

Open a URL in a browser.

api.openURL("http://cavalry.scenegroup.co");
api.openURL('file:///Users/User/Desktop');

isGuiSession() → bool

Returns true if this is a GUI session of the app, and false if not (e.g the CLI is running).

console.log(api.isGuiSession());

getPlatform() → string

Get the current hardware platform. This will return either macOS or Windows

console.log(api.getPlatform());

getSystemInfo() → {cpu:string, os:string, uniqueId:string}

Return the current hardware system info as an object with the following keys:

  • cpu - The current CPU architecture (e.g. "arm64").
  • os - The current OS (e.g. "macos").
  • uniqueId - A unique identifier that can be used to track a machine for an extended period of time – useful for network operations. Note that this value may change between reboots.
console.log(JSON.stringify(api.getSystemInfo()));

getCavalryVersion() → string

Return the Cavalry version as a string (e.g. "1.5.6").

console.log(api.getCavalryVersion());

toNativeFilePath(filePath:string) → string

Convert a macOS file path to a Windows file path. This replaces / with \ (and escapes them as needed).

console.log(api.toNativeFilePath("/path/to/my folder"));
// This will return "\path\to\my folder" on Windows.

serialise(layerIds:[string], withConnections:bool)

Convert a list of Layers into their 'save file' representation.

The first argument is an array of strings [string] containing the layers to be serialised. The second argument is withConnections - if this is true, any input connections will also be serialised. For example, if a Shape in the array is connected to a Color Array, setting withConnections to true will also serialise the Color Array.

var primId = api.primitive("polygon", "My Polygon");
api.set(primId, {"material.materialColor": "#8dc429"});
api.writeToFile("/Users/username/Desktop/textExport.txt", api.serialise([primId], false));
Save as .cvc

By saving serialised files with a .cvc extension, they can then be imported back into a Scene via the File > Import Scene... menu. Files saved with any other extension can be imported using the deserialised function.

deserialise(json:string)

Deserialise a JSON string to Layers. This function is the opposite of serialise.

// Select a Layer
let sel = api.getSelection();
let str = api.serialise(sel, true);
api.deserialise(str);

setPreferenceObject(key:string, preferences:object)

Save a preference.

const hello = {first:"Hello, ", second:"World", third:"!"};
api.setPreferenceObject("testKey", hello);

hasPreferenceObject(key:string) → bool

Query to see if preference exists.

const hello = {first:"Hello, ", second:"World", third:"!"};
api.setPreferenceObject("testKey", hello);
console.log( api.hasPreferenceObject("testKey") );

getPreferenceObject(key:string) → object

Return any existing preferences.

const hello = {first:"Hello, ", second:"World", third:"!"};
api.setPreferenceObject("testKey", hello);
const myPrefs = api.getPreferenceObject("testKey");
console.log(myPrefs.first+myPrefs.second+myPrefs.third);

setUserData(layerId:string, key:string, value:object)

Save arbitrary data to a layer. This could be a string, or an object.

var primId = api.primitive("star", "Star");
api.setUserData(primId, "test", "Hello, World!");

hasUserDataKey(key:string) → bool

Query to see if user data with a given key exists.

var primId = api.primitive("star", "Star");
api.setUserData(primId, "test", "Hello, World!");
console.log(api.hasUserDataKey(primId, "test"));

getUserDataKey(layerId:string, key:string) → object

Return any existing user data for the given key.

var primId = api.primitive("star", "Star");
api.setUserData(primId, "test", "Hello, World!");
console.log(api.getUserDataKey(primId, "test"));

getLayerFromUUID(uuid:string) → string

Each layer in Cavalry has a unique identifier (a UUID). Layers based on the UUID can be identified with this API. See the example for how to get a UUID from a layer.

var primId = api.primitive("star", "Star")
let uuid = api.get(primId, "uuid")
let idCheck = api.getLayerFromUUID(uuid);
console.log(primId+" === "+idCheck);

Timer(callback:object)

Timers can be used in UI scripts and can be useful for polling Web APIs. When the Timer is triggered it will call an onTimeout() function on the Timer object. Implement this function to have the Timer execute logic when this happens.

Functions:

  • start() // start the timer.
  • stop() // stop the timer.
  • isActive() // returns if the timer is currently running.
  • setInterval(interval:int) // set how long the timer is (in milliseconds).
  • setRepeating(repeat:bool) // set if the timer is repeating (true by default).
  • onTimeout() // Implement this callback function on a timer object and it will be called when the timer runs out.
// Define a callback class to be used by the timer
function Callbacks() {
// This callback will be called whenever the timer times out
this.onTimeout = function () {
console.log("Timer Expired");
}
}

// Create the callback class
var callbackObj = new Callbacks();

// Make the timer and feed it the callback object
var timer = new api.Timer(callbackObj);

isAltHeld() → bool

Return whether or not the Option/Alt key is held when a script is run.

info

isAltHeld() cannot be tested in the JavaScript Editor. It can only be tested in a Script run from the Script menu, or from a UI script launched from the JavaScript Editor. This is because Cavalry intercepts Option/Alt clicks for native actions.

isShiftHeld() → bool

Return whether or not the Shift key is held when a script is run.

isControlHeld() → bool

Return whether or not the Control (Windows) or Command (macOS) key is held when a script is run.

isMetaHeld() → bool

Return whether or not the Control (Windows) or Command (macOS) key is held when a script is run.

getAllLayerTypes(includeExperimentalTypes:bool) → [object]

Return all the user facing types and names of Layers in Cavalry.

let layerTypes = api.getAllLayerTypes(false);
for (data of layerTypes) {
console.log(data.name + ". Type: " + data.type);
}

// data.type - the internal Layer type.
// data.name - the English name of the Layer.

encrypt(textToEncrypt:string) → string

Encrypt text. Useful for script authors to automate the encryption of scripts.

console.log(api.encrypt("Hello."));