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 propertiesRig propertiesRigging propertiesRig controllers propertiesRigging weights propertiesRigging mesh propertiesRig items propertiesRig splines propertiesRig simulation propertiesRig properties propertiesRig drivers propertiesRig draw order propertiesTransform propertiesTrim path properties
    Error codesBudgets
    Changelog
    API overviewAuthentication and keysRate limits and errorsUploading assetsFoldersUploading templatesTriggering a flowRuns and creativesAPI Request deliveryCallbacks
    All docs
    Motion Scripting
    / Objects

    Project

    The document handle: reaching every comp in the project and the seed that steers all randomness.

    The document.

    Table of contentsseedWorked example: One dial for every randomnumCompsWorked example: Split across the document

    seed#

    Type: number (field).

    The document expression seed.

    Worked example: One dial for every random#

    The scene. One orange square resting on a dark 640 by 360 stage.

    • The comp Drift: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • Square: a solid layer, at [320, 180], 60 by 60, filled #f2a833.

    The script. Add a project script named House shuffle and paste:

    export const outputs = ["layer_1:transform.position"];
    
    export function frame() {
      seedRandom(project.seed + 7);
      const jx = (random() - 0.5) * 160;
      const jy = (random() - 0.5) * 90;
      return [value[0] + jx, value[1] + jy];
    }
    

    Line by line:

    • Line 4: project.seed is the document's expression seed - one number shared by every script. Folding it into seedRandom() means changing that single document setting reshuffles every seeded pattern at once, while leaving it alone keeps every render identical.
    • Lines 5-7: The usual centred jitter, now under the house seed plus this script's own offset of 7 so it never mirrors another script's stream.

    What you see. The square jitters deterministically as ever - but bumping the document's expression seed deals a completely fresh pattern to this and every other seeded script at once.

    The square jitters deterministically as ever - but bumping the document's expression seed deals a completely fresh pattern to this and every other seeded script at once.

    numComps#

    Type: number (field).

    The composition count. For a COMPONENT script this is 1 - its closure is its own resolved comp, never the host document's count.

    Worked example: Split across the document#

    The scene. A Main comp holding a wide banner, and a second Card comp holding a small badge, so one project script can drive layers in both.

    • The comp Main: 640 by 360 at 25 fps, 100 frames (4 seconds).
    • The comp Card: 320 by 180 at 25 fps, 100 frames (4 seconds).
    • Banner in Main: a solid layer, at [320, 300], 400 by 40, filled #2ec4b6.
    • Badge in Card: a solid layer, at [160, 90], 60 by 60, filled #ff5d73.

    The script. Add a project script named Fair share and paste:

    export const outputs = ["layer_1:transform.opacity"];
    
    export function frame() {
      return 100 / project.numComps;
    }
    

    Line by line:

    • Line 4: numComps counts the whole document's comps - two here, so the Banner takes a half share of 50. One caution for component authors: inside a COMPONENT script the project is the component's own closed world and numComps reports 1.

    What you see. The banner renders at 50 percent - a half share because the document holds two comps.

    The banner renders at 50 percent - a half share because the document holds two comps.