tvos-assets

Generate complete Images.xcassets bundles with parallax tvOS icons, iOS 18+ appearance variants, Top Shelf, and splash assets, plus the iOS 27 App Store creative assets with looping videos. Expo-first: the config plugin regenerates all of it on every prebuild.

What Goes In

you bring

  • ├─icon.svg
  • │or
  • │  ├─layer-middle.svg
  • │  └─layer-front.svg
  • ├─background.png
  • ├─#F39C12
  • ├─artwork.svgApp Store
  • └─music.mp3App Store

Two layers give real parallax, and the flat icon is built from them; --icon uses your own. The App Store art and music are optional.

tvos-assets writes

  • ├─AppIcon.brandassets/30
  • ├─AppIcon.appiconset/4
  • ├─SplashScreenLogo.imageset/6
  • ├─SplashScreenBackground.colorset/1
  • ├─icon.png · preview.html3
  • └─AppStore/9

44 files, plus 9 with --app-store: 5 images and 4 looping videos.

Three Ways To Run

// app.json: regenerates on every expo prebuild
"plugins": [
["tvos-assets/plugin", {
"icon": "./assets/brand/icon.svg",
"background": "./assets/brand/background.png",
"color": "#F39C12",
"layers": {
"front": "./assets/brand/layer-front.svg",
"middle": "./assets/brand/layer-middle.svg"
},
"appStore": {
"header": { "source": "./store/artwork.svg", "animate": { "rows": 20 } }
}
}]
]
EXPO_TV=1 → tvOS brand assets + Info.plist keys · plain prebuild → iOS appiconset
appStore → ./AppStore, outside ios/, unchanged files skipped

App Store Creative Assets

New in 1.6.0: --app-store also writes the iOS 27 and iPadOS 27 creative assets into AppStore/, beside Images.xcassets. Every placement App Store Connect takes is covered, each as an opaque PNG with the art inside the safe areas from Apple's creative asset templates, and the ones that take video get a seamless loop.

PlacementImageVideoDefault
Product page headerheader.png 3840 × 1646header.mp4, 5-30 son
Search resultssearch-results.png 3840 × 2560search-results.mp4, 5-30 son
Universal (header and search results in one)universal.png 5244 × 2950none, PNG onlyon
In-App Event cardevent-card.png 3840 × 2160event-card.mp4, 15-30 soff, needs source
In-App Event details pageevent-details.png 2160 × 3840event-details.mp4, 15-30 soff, needs source

header.png 3840 × 1646

Product page header

Video: header.mp4, 5-30 s

Default: on

search-results.png 3840 × 2560

Search results

Video: search-results.mp4, 5-30 s

Default: on

universal.png 5244 × 2950

Universal (header and search results in one)

Video: none, PNG only

Default: on

event-card.png 3840 × 2160

In-App Event card

Video: event-card.mp4, 15-30 s

Default: off, needs source

event-details.png 2160 × 3840

In-App Event details page

Video: event-details.mp4, 15-30 s

Default: off, needs source

Below is what the published 1.7.0 package wrote for TomoTV's own listing: one SVG of row artwork for the header, search results and universal images, another for a Live TV In-App Event, every video a 20 s loop with music. Header and search cards rise and sink; the event cards slide in rows. These are web copies, so tap a video for sound; the originals are full-size H.264 High.

header.mp43840 × 1646 · 20 s loop
search-results.mp43840 × 2560 · 20 s loop
event-details.mp42160 × 3840 · 20 s loop
event-card.mp43840 × 2160 · 20 s loop
App Store header still: three rows of rounded yellow cards on black, the TOMO TV card right of centre
header.png3840 × 1646
App Store search results still: the card rows re-tiled to a 3:2 canvas around the TOMO TV card
search-results.png3840 × 2560
App Store universal still: the card rows shifted left so the TOMO TV card fits the narrower safe area
universal.png5244 × 2950
In-App Event card still: scan-lined yellow cards with a red live dot on one card, no text
event-card.png3840 × 2160
In-App Event details still: a portrait wall of scan-lined yellow cards with a red live dot on one card
event-details.png2160 × 3840

These files are in use: Tomo TV's App Store listing runs them, so the header and search results there are this tool's output.

Download on the App Store

Row artwork

Row artwork is an SVG whose <mask>holds rows of one repeated card over full-canvas fills, the wall of cards a Figma or Sketch export gives you, with anything fixed drawn on top. One file serves every placement: it is re-tiled to each canvas and centred on that placement's art safe area. Add animate: { rows: 20 } and the rows slide one card per loop, alternating direction, while the fixed layer stays put, so the last frame runs into the first. A shape drawn on top of one card rides its row, like the live dot above. Every rule is checked, and a file that breaks one fails with a message naming it.

Scene artwork

New in 1.10.0. A scene is any other SVG, a night sky or a wordmark on a gradient, cover-fitted to each canvas. animate: { loop: 20, elements: [...] } makes its video: each rule selects elements of the file (#id, tag or #id tag) and writes an <animate>on every one of them in SMIL's own words, attributeName, values, dur, additive and the rest, with staggerspreading the starts so no two neighbours move in step. The frames come from the Google Chrome on your machine, the file's animations paused and seeked to each frame's time, so the video is what Safari and Chrome play when they open the SVG. The file as played is written beside the still, so the loop can be previewed in a browser before it renders. An SVG that already carries its own <animate> tags needs no rules at all.

A fast renderer

Only the cards move, so each frame is blended from the artwork drawn once with every card lit and once with none, through a card mask built from row strips, several frames at a time. A 20 s 4K header loop takes about 30 s on an M1 Max. Art the blend cannot reproduce exactly is drawn in full each frame instead. Videos encode as H.264 High on libx264, whose quality holds steady across keyframes so the loop point does not hitch, or as ProRes 422 HQ. A simulator recording works too: header.video or searchResults.video is cut into the same kind of seamless loop.

Looped music

video.audioputs music on every video instead of the silent track, looped so it never cuts: each video takes a stretch of the track one second longer than itself and crossfades that last second into its first. The stretch sits where the beat a loop length later lines up with the beat at its start, and the level never drops below half the music's median.

From the Expo plugin

Give the plugin an appStore prop and every expo prebuild writes the creative assets into its outDir (default ./AppStore), outside ios/ so prebuild never wipes them. Unchanged files are skipped, so the videos only render again after their artwork or settings change.

One Config, Three Surfaces

Expo plugin

Add it to app.json. Every expo prebuild regenerates the full catalog, nothing gets committed under ios/.

CLI

One command, one zip. Made for scripts and CI, with --dry-run and --quiet included.

Config file

tvos-assets.config.json, read by both. Auto-discovered in the project root, schema included.

When the same setting arrives from two places, the more direct one wins: the config file overrides the defaults, --set overrides the config file, and --icon, --background, and --color always win.

CLI Reference

Inputs

FlagDescriptionDefault
--icon <path>Icon PNG or SVG (transparent background). Optional when --layer-front and --layer-middle are both given: the icon is assembled from them instead.required
--background <path>Background PNG or SVGrequired
--color <hex>Splash background color, light moderequired
--dark-color <hex>Splash background color, dark modeauto-darkened
--icon-dark <path>iOS dark-appearance icon overridederived from --icon
--icon-tinted <path>iOS tinted-appearance icon overridegrayscale of --icon
--icon-border-radius <px>Icon corner radius (0 = square)0
--layer-front <path>Custom front parallax layer art—
--layer-middle <path>Custom middle parallax layer art—
--layer-back <path>Custom back parallax layer art—

--icon <path>

Icon PNG or SVG (transparent background). Optional when --layer-front and --layer-middle are both given: the icon is assembled from them instead.

Required

--background <path>

Background PNG or SVG

Required

--color <hex>

Splash background color, light mode

Required

--dark-color <hex>

Splash background color, dark mode

Default: auto-darkened

--icon-dark <path>

iOS dark-appearance icon override

Default: derived from --icon

--icon-tinted <path>

iOS tinted-appearance icon override

Default: grayscale of --icon

--icon-border-radius <px>

Icon corner radius (0 = square)

Default: 0

--layer-front <path>

Custom front parallax layer art

--layer-middle <path>

Custom middle parallax layer art

--layer-back <path>

Custom back parallax layer art

Output

FlagDescriptionDefault
--config <path>Config JSON file./tvos-assets.config.json if present
--output <path>Directory for the zip file~/Desktop
--out-dir <path>Write Images.xcassets directly, no zip—
--mode <zip|dir>Output mode; --out-dir implies dirzip
--platforms <list>Icon families: tvos, ios, or bothboth
--preview / --no-previewWrite preview.html contact sheeton for zip, off for --out-dir

--config <path>

Config JSON file

Default: ./tvos-assets.config.json if present

--output <path>

Directory for the zip file

Default: ~/Desktop

--out-dir <path>

Write Images.xcassets directly, no zip

--mode <zip|dir>

Output mode; --out-dir implies dir

Default: zip

--platforms <list>

Icon families: tvos, ios, or both

Default: both

--preview / --no-preview

Write preview.html contact sheet

Default: on for zip, off for --out-dir

App Store

FlagDescriptionDefault
--app-storeWrite the iOS 27 and iPadOS 27 App Store creative assets into AppStore/off
--app-store-background <path>Backdrop for the creative assets--background
--app-store-center <path>Centre art, such as a wordmark, contain-fit in each safe areathe icon

--app-store

Write the iOS 27 and iPadOS 27 App Store creative assets into AppStore/

Default: off

--app-store-background <path>

Backdrop for the creative assets

Default: --background

--app-store-center <path>

Centre art, such as a wordmark, contain-fit in each safe area

Default: the icon

Naming & selection

FlagDescriptionDefault
--brand-name <name>Name of the .brandassets bundleAppIcon
--ios-icon-name <name>Name of the iOS .appiconsetAppIcon
--splash-logo-name <name>Splash logo imageset nameSplashScreenLogo
--splash-background-name <name>Splash colorset nameSplashScreenBackground
--splash-logo-size <px>Base splash logo size200
--no-ios-iconSkip the iOS appiconset—
--no-top-shelfSkip both Top Shelf imagesets—
--no-splashSkip splash logo and colorset—

--brand-name <name>

Name of the .brandassets bundle

Default: AppIcon

--ios-icon-name <name>

Name of the iOS .appiconset

Default: AppIcon

--splash-logo-name <name>

Splash logo imageset name

Default: SplashScreenLogo

--splash-background-name <name>

Splash colorset name

Default: SplashScreenBackground

--splash-logo-size <px>

Base splash logo size

Default: 200

--no-ios-icon

Skip the iOS appiconset

--no-top-shelf

Skip both Top Shelf imagesets

--no-splash

Skip splash logo and colorset

Advanced

FlagDescriptionDefault
--set <path=value>Override any config key by dotted path; repeatable—
--dry-runReport what would be written, then exit—
--print-configPrint the fully merged config as JSON—
--init [path]Write a starter tvos-assets.config.json—
--quietOnly errors and the final output path—

--set <path=value>

Override any config key by dotted path; repeatable

--dry-run

Report what would be written, then exit

--print-config

Print the fully merged config as JSON

--init [path]

Write a starter tvos-assets.config.json

--quiet

Only errors and the final output path

Questions

What do I need installed?

Node.js 18 or later. sharp installs with the package, and ffmpeg is only needed for the App Store videos. Xcode is where the generated Images.xcassets ends up, not something the tool runs.

Does it work with Expo and React Native tvOS?

Yes. The tvos-assets/plugin entry in app.json regenerates the catalog on every expo prebuild, for tvOS (EXPO_TV=1) and for iOS. Each prebuild rewrites only the folders it owns and leaves the rest of the catalog alone.

Can the inputs be SVG?

Yes: the icon, the background, the iOS appearance overrides and the parallax layers. An SVG is rasterized at the density each output needs, so a small viewBox still makes a sharp 4640px Top Shelf image, and the minimum sizes do not apply to it.

How big should PNG inputs be?

The icon at least 1024x1024, on transparency. The background at least 2320x720; 4640x1440 matches the Top Shelf image at @2x and avoids upscaling. Anything smaller still runs, with a warning.

How are the dark and tinted iOS icons made?

Both are derived. Dark is your icon on transparency, because iOS draws the dark backdrop itself. Tinted is a grayscale render that iOS colors with the user's tint. Replace either with your own art through --icon-dark and --icon-tinted.

How do the parallax layers work?

A tvOS app icon is an imagestack of three layers, Front, Middle and Back, that the home screen tilts on focus. By default Front and Middle both carry the whole icon. Give each its own art with --layer-front and --layer-middle for real depth; with both set, --icon is optional and the flat icon is assembled from them.

What does --app-store add?

The iOS 27 and iPadOS 27 creative assets App Store Connect takes: product page header, search results, universal, and the In-App Event card and details. Each is an opaque PNG with the art inside Apple's safe area, and the placements that take video get a seamless loop.

Is it used in a shipped app?

Yes. Tomo TV's app icons, Top Shelf images, splash screen and App Store listing art are all written by this tool, and the app is live on the App Store.