📐

Scale
Design-resolution-aware scaling for numbers, arrays, and AAs.

scale multiplies a numeric value by (actual device display width) / (configured design-resolution width), computed once at app boot — so a layout authored at one resolution renders proportionally correct at another.

Setup — config file and manifest, both required

flash-theater.config.json declares the baseline your pixel values were authored against:

flash-theater.config.json
{
  "designResolution": "hd"
}

The Roku manifest's ui_resolutions must declare every tier you want scale to ever do anything for — a single-tier manifest makes Roku's own OS-level auto-scaling absorb the difference first, silently turning every scale call into a permanent no-op:

manifest
# manifest — MUST declare every resolution tier you want scale to act on
ui_resolutions=fhd,hd

Declaration form

scale field/state/derived/watch/read — scales a number directly, or an array/assocarray element-wise, one level deep.

BounceButtonDemo.thr (excerpt)
scale field cardWidth: integer = 300
scale field cardHeight: integer = 180
scale derived cardTranslation: object = [810, 450]

Statement form

A template {expr} binding can't be scaled directly — compute the scaled value once via scale <local> = <expr> and store it on the data instead:

ScheduleList.thr (excerpt)
private function renumbered(days: object): object {
  updated = []
  for (i = 0 to days.Count() - 1) {
    day = days[i]
    scale y = i * 40
    day.y = y
    updated.Push(day)
  }
  return updated
}

Reference implementation — every app, not just one

Unlike every other topic on this site, scale has no single dedicated chapter app of its own — it's used pervasively across all 14 apps/* workspaces (every rootWidth/rootHeight, every translation, every chapter app's own layout), since a real device pass needs every screen scaled consistently, not just one demo of the mechanism in isolation. apps/animation-demo's BounceButtonDemo.thr and apps/sample-app's ScheduleList.thr (the two files excerpted above) are two ordinary, representative examples, not special cases. See findings/scale-config-and-codegen.md for the compile-time design and findings/scale-device-verification.md for the live-device lessons (manifest tiers, designResolution choice, partial-scale focus breakage) — the closest thing this feature has to a "which chapter covers this" index, since the real answer is "all of them."

⚠️ Not (yet) supported

  • ○ node-typed fields are excluded from scaling entirely.
  • ○ Integer scaling truncates toward zero, never rounds — 10 * 0.667 = 6, not 7.
  • ○ Arrays/assocarrays scale one level deep only — a nested array's inner values pass through unscaled.
  • ○ No compile-time type check on scale derived/watch/read — a scaled non-numeric expression is a silent runtime no-op, not a diagnostic.
  • ○ scale watch/read have an unresolved double-scaling risk if a store value was already scaled by whoever wrote it.
  • ○ Partial adoption across a screen with cross-component focus/LRUD dependencies can break directional navigation between a scaled and an unscaled sibling — treat scale adoption as closer to all-or-nothing per app.

Exact grammar: GRAMMAR.md. Full feature status: docs/features.md.