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

    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.