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
    / Language

    Formatting values

    House recipes for turning numbers into countdown, percentage and timecode strings without the Intl tables the sandbox omits.

    Scripts often turn numbers into readable strings - a countdown, a percentage, a timecode for a diagnostic log. The engine deliberately ships without Intl (locale tables would make renders differ between machines - see JavaScript support), and plain JavaScript covers the ground well. These are the house recipes.

    Numbers#

    const price = 1234.5;
    
    price.toFixed(2);            // "1234.50" - fixed decimals
    String(Math.round(price));   // "1235" - whole numbers
    String(price).padStart(8, " "); // "  1234.5" - column alignment
    

    For thousands separators, group by hand - three digits at a time from the right:

    function thousands(n) {
      const [whole, part] = Math.abs(n).toFixed(0).split(".");
      const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
      return (n < 0 ? "-" : "") + grouped;
    }
    
    thousands(1234567); // "1,234,567"
    

    Deterministic and identical on every machine - which is the point.

    Padding and counters#

    String(7).padStart(3, "0");   // "007"
    String(42).padEnd(5, ".");    // "42..."
    

    Timecodes#

    Build timecodes from the canonical clock - frame and fps - never from float seconds:

    export function frame({ frame, fps }) {
      const total_seconds = Math.floor(frame / fps);
      const minutes = String(Math.floor(total_seconds / 60)).padStart(2, "0");
      const seconds = String(total_seconds % 60).padStart(2, "0");
      const frames = String(frame % fps).padStart(2, "0");
      const timecode = minutes + ":" + seconds + ":" + frames;
      console.log(timecode); // "00:01:05" at frame 1625 of a 25 fps comp
      return value;
    }
    

    The same shape gives you countdowns (total_seconds counting down from the comp's duration) and cue labels.

    Where formatted strings go#

    Text CONTENT is in the drive set: declare a text layer's content.text as an output and return the formatted string, and the layer's source text follows the script per frame. Swap the console.log above for a return on such an output and the timecode becomes a live caption; the frame reference's countdown example drives one digit layer the same way. Text takes a per-frame HOLD (there is nothing to interpolate between two strings), so return values that change exactly when you mean them to - whole seconds, not raw float time. Formatted strings also serve diagnostics (console.log) and comparisons, and the quick start's countdown shows the multi-layer variant with five digit layers and one script.