360° video app and AI video server for an installation art exhibition

Period  — 

Each artwork captured visitors' photos and footage differently. On‑site apps and generation jobs stay separate per artwork, while a shared server, operations dashboard and results website serve them all.

Unattended on‑site app that stitches camera SD card footage and delivers it by QR

SwiftSwiftUIPythonFastAPIPostgreSQLAWS S3TypeScript

  • Nalda · Swift on‑site app for the balloon‑chair experience
  • Namu Block · server API creating roughly 15‑second videos from images and words
  • Bibi Hero · server API creating a character poster from a photo, then a roughly 10‑second video with the selected motion
  • Reels13 · server job that turns one chosen take out of three 13‑second takes into AI video
  • 360° balloon experience results website · mobile page for visitors to collect videos by QR
  • Operations dashboard · orders, failures, retries and AI costs by location, device and service

Visitors waiting for results

Problem
Manually transferring and editing camera files made visitors wait for their results.
Solution
When the SD card goes in, the app picks only new files, removes duplicates and stitches footage in the background. Stitching and uploading at once could overload the on‑site PC, so stitching runs one job at a time. It uploads a preview first to show the QR, followed by the original.

The previous visitor's QR

Problem
The next visitor could scan the previous visitor's QR before it was replaced.
Solution
Replaced the old QR with a standby video after a set interval. Connecting the camera displays copy progress before showing a new QR.

AI providers and failed requests

Problem
External AI services could change or fail. Changing providers should not mean rewriting both the server code and the job queue.
Solution
Chose external services over our own GPUs and kept provider names out of the code so switching providers changes only the connection layer. Failed requests are retried up to 3 times after the first attempt, and authentication, configuration and billing errors are not retried. The same rule is written into the code, the docs and the dashboard. A repeated request does not create a second generation job.