Nubu
  • Features
  • How it works
  • Pricing
  • Integrations
  • FAQ
  • Contact
  • Docs
  • Blog
  • Log in
  • Log inSign up
    Nubu

    Creative automation for teams that ship campaigns, not busywork.

    Try for free
    Product
    FeaturesNubu MotionHow it worksPricingIntegrationsFAQ
    Company
    AboutContactDocsBlogLog in
    Legal
    Privacy policyTerms of serviceData deletion
    © 2026 Bear Studios · Nubu
    Introduction
    Templates overviewInstalling Nubu BuilderAdding template compsAdding propertiesAdding footageSetting defaultsExporting your templateUploading a template
    Flows overviewAvailable nodesAdding a nodeConnecting nodesWorking with dataTemplates and outputsUsing AI nodesBuilding a creative
    CampaignsTemplatesAssetsGlossariesRendersSettings
    Connecting Meta AdsConnecting Google AdsConnecting OpenAIConnecting Gemini AI StudioConnecting Anthropic
    AI Assistant overviewPrompt examplesAssistant settings and controls
    Nubu Motion scripts: the reference
    OverviewQuick startDifferences from After EffectsDeterminism
    JavaScript supportReferences and idsProject scriptsState and timeBudgets and errorsFormatting values
    GlobalTime conversionInterpolationVector mathsRandom and noiseColour conversionOther maths
    LayerCompPropertyKeyframePathGeometryProjectComponentConsoleComponentControlExpressionControl
    The property vocabularyAnimators propertiesAudio propertiesContent propertiesContents propertiesEffects propertiesExpression controls propertiesFills propertiesInstance controls propertiesLayer propertiesLayout propertiesMasks propertiesStrokes propertiesText style propertiesTransform propertiesTrim path properties
    Error codesBudgets
    Changelog
    All docs
    Motion Scripting
    / Objects

    Component

    The handle an embedded script uses to read its own component instance and the controls people set on it.

    A component handle (per-instance PR). Control values read WIRE units: scalar number, text string, point [x, y], colour [r, g, b, a] in 0..1.

    Table of contentsidWorked example: The definition's stable idnameWorked example: Read the definition's nameinstanceWorked example: Definition or placed instancecontrolsWorked example: Walk the exposed controlsnumControlsWorked example: The cheap censuscontrolWorked example: One control by idcontrolByLabelWorked example: Find a control by its label

    id#

    Type: string (field).

    The definition id.

    Worked example: The definition's stable id#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Definition check and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      return thisComponent.id === "component_1" ? 85 : 10;
    }
    

    Line by line:

    • Line 4: A component definition carries a stable id like every other entity. This embedded script rides inside the Badge definition, so wherever a Badge is placed the check passes and the face renders at 85. Ids survive library publishing and importing - the imported copy is re-keyed consistently, scripts included.

    What you see. The badge face renders at 85 percent opacity everywhere the component appears - in the definition editor and in every placed instance.

    The badge face renders at 85 percent opacity everywhere the component appears - in the definition editor and in every placed instance.

    name#

    Type: string (field).

    The definition name.

    Worked example: Read the definition's name#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Name seat and paste:

    export const outputs = ["layer_1:transform.position"];
    
    export function frame() {
      return [value[0], 100 + thisComponent.name.length * 6];
    }
    

    Line by line:

    • Line 4: name is the definition's display label - "Badge", five characters, dropping the face 30 pixels below its resting line. As everywhere, the name is data to read; the component's identity for references is its id.

    What you see. The badge face sits at y 130 - six pixels of drop for each letter of the component's name.

    The badge face sits at y 130 - six pixels of drop for each letter of the component's name.

    instance#

    Type: string | null (field).

    The instance layer id this evaluation belongs to; null under the definition context (the internal comp evaluated directly - defaults).

    Worked example: Definition or placed instance#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Context lamp and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      return thisComponent.instance === null ? 40 : 70;
    }
    

    Line by line:

    • Line 4: instance tells a component script WHERE it is running: null while the definition's own internal comp is being evaluated (the editing context, control defaults in force), or the placing layer's id when a real instance renders. Here the face rests at a hushed 40 in the definition editor and at 70 inside every placed Badge - one script, two honest contexts.

    What you see. Editing the Badge definition shows its face at a hushed 40 percent; every placed instance in a comp renders it at 70 - the same script reading its context.

    Editing the Badge definition shows its face at a hushed 40 percent; every placed instance in a comp renders it at 70 - the same script reading its context.

    controls#

    Type: ComponentControl[] (field).

    Every control in definition order with its effective value.

    Worked example: Walk the exposed controls#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Control census and paste:

    export const outputs = ["layer_1:transform.scale"];
    
    export function frame() {
      const grown = 100 + thisComponent.controls.length * 20;
      return [grown, grown];
    }
    

    Line by line:

    • Line 4: controls lists every control the definition exposes, in definition order, each carrying its EFFECTIVE value for this context. The Badge exposes one (Amount), so the face renders at 120 percent. Iterate the array to make a component adapt to its own surface.

    What you see. The badge face renders a fifth larger - 20 percent of growth for the component's one exposed control.

    The badge face renders a fifth larger - 20 percent of growth for the component's one exposed control.

    numControls#

    Type: number (field).

    controls.length.

    Worked example: The cheap census#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Surface size and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      return 60 + thisComponent.numControls * 10;
    }
    

    Line by line:

    • Line 4: numControls is controls.length without building the array. One exposed control lifts the face to 70 percent; expose more and it brightens with the surface.

    What you see. The badge face rests at 70 percent - sixty as the floor plus ten for its single exposed control.

    The badge face rests at 70 percent - sixty as the floor plus ten for its single exposed control.

    control#

    control(id: string): ComponentControl | null
    

    The control by id, or null.

    Worked example: One control by id#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Amount slide and paste:

    export const outputs = ["layer_1:transform.position"];
    
    export function frame() {
      const amount = thisComponent.control("c_amount");
      return [40 + amount.value, value[1]];
    }
    

    Line by line:

    • Line 4: control(id) fetches one exposed control by its definition id, or null when no such control exists - the polite lookup, like layer.control().
    • Line 5: The face slides right by the EFFECTIVE Amount: 20 (the default) while editing the definition, 65 inside the placed instance that overrides it. Same script, per-context values - this is how one component serves every instance differently.

    What you see. In the definition editor the face sits at x 60; the placed Badge in the Host comp shows it at x 105, because that instance's Amount is overridden to 65.

    In the definition editor the face sits at x 60; the placed Badge in the Host comp shows it at x 105, because that instance's Amount is overridden to 65.

    controlByLabel#

    controlByLabel(label: string): ComponentControl | null
    

    The first control with this label, or null.

    Worked example: Find a control by its label#

    The scene. A Badge component whose internal comp holds one blue face layer, exposed with a scalar Amount control, plus one Badge instance placed in the Host comp with Amount overridden to 65.

    • The comp Host: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Badge internals: 200 by 200 at 25 fps, 100 frames (4 seconds).
    • Badge face in Badge internals: a solid layer, at [100, 100], 120 by 120, filled #4d9de0.
    • Badge in Host: a component instance, at [320, 180].
      • The instance overrides the component's c_amount control to 65.
    • The component Badge wraps Badge internals and exposes 1 control: Amount (scalar, default 20).

    The script is EMBEDDED in the Badge component - add it from the component's own Scripts group, and it travels with the component wherever it is placed.

    The script. Add a component script named Label lookup and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      const amount = thisComponent.controlByLabel("Amount");
      return amount ? amount.value + 15 : 100;
    }
    

    Line by line:

    • Line 4: controlByLabel() searches by the HUMAN label instead of the id, answering the first match or null. Labels can repeat and can be edited, so prefer control(id) in anything long-lived - the label form suits quick sketches and generic helper components.
    • Line 5: The default Amount of 20 plus 15 rests the face at 35 percent in the definition context.

    What you see. The badge face rests at 35 percent opacity in the definition editor - the Amount control found by its label, plus fifteen.

    The badge face rests at 35 percent opacity in the definition editor - the Amount control found by its label, plus fifteen.