The animated MacBook demo on the Influxx marketing homepage used to be a beautifully faked terminal — a scripted Claude Code TUI drawn frame-by-frame onto a canvas, styled to look like Ghostty. As part of Influxx 1.2 the team rebuilt the renderer to draw the app's actual native chat surface instead: real --native-chat-* color tokens, real user bubbles, real serif assistant prose, a real composer pill. The script — the same typed prompts, the same tool calls, the same timings — didn't change at all. Only the pixels did.
A Demo Is a Rendering Layer Over a Script
Look at influxx-screen-demo.ts and the split is obvious once you see it: a PaneScript — an ordered list of typed prompts and streamed lines, each stamped with a timestamp — and a set of draw functions that turn that script into pixels for a given moment t. The whole animation is a pure function of time: feed it a number of seconds and it paints exactly what the demo should look like at that instant, deterministically, with no hidden state carried between frames.
That separation is what made the rewrite possible without touching the content. The two PaneScript objects — PANE1, an agent adding a Stripe checkout flow, and PANE2, a second agent spawned to run a note's open items — describe what happens: which lines appear, when, in what color, whether they're a tool call or assistant prose. Nothing in either script says how a line should be drawn. That's the renderer's job, and it's the only thing that changed.
What the Terminal Renderer Used to Draw
The original renderer painted each script line as raw terminal text: Ghostty's dark theme background, SF Mono type, a blinking box-drawing welcome banner, ⏺ tool-call markers with ⎿ result lines underneath, and a spinning ✻ "thinking" glyph cycling through star-shape frames. It was a faithful terminal simulation — and it was showing off a surface that isn't actually where most Influxx users spend a chat session, since native chat is the primary way agents are driven day to day.
Classifying the Script Instead of Rewriting It
The rebuild's central function is classifyChatLine, and what it does is deliberately narrow: it takes a raw terminal-style line from the existing script and decides what kind of chat block it should become — user, prose, tool, result, or working — without altering the underlying text. Shell noise like the welcome banner's box-drawing characters and the leading $ claude line is filtered out entirely, since a chat transcript has no equivalent for "you typed a command into a shell." Lines prefixed with > become user bubbles. Lines with a ⏺ marker get pattern-matched against a tool-call shape (Name(args), or the literal Update Todos / Bash(...) forms) to decide whether they're a tool invocation or plain assistant prose. Spinner lines — the ones with an until timestamp, previously rendered as the animated ✻ Thinking… star — become a working block with the same live label, just rendered as a small accent-colored spinner instead of a text glyph.
Nothing about when a line appears, what it says, or how long it stays on screen changes. classifyChatLine only decides which chat-shaped bucket an existing line falls into.
"The tempting version of this project is to open a blank file and write a new demo that looks like native chat. We didn't do that, on purpose. We kept the exact same
— Priya Ramaswami, Staff Engineer, Source Control at ETAPXPaneScriptobjects — same prompts, same tool calls, same timestamps — and wrote a classifier that reads those same lines and decides how to draw them differently. If we'd started from scratch, we'd have had to re-verify every claim the demo makes about the product all over again. Reusing the script means we only had to verify it once."
Matching the Real App's Tokens, Not Approximating Them
The new drawNativeChatPane function pulls its palette from a CHAT constant whose values are commented as mirroring the app's actual --native-chat-* CSS custom properties: background, surface, composer fill, text, muted text, faint text, and a hairline border color, plus the shared mint accent used across the product. User turns render as right-aligned bubbles on the chat surface color with sans-serif type; assistant prose renders left-aligned in a serif font (Georgia/Times New Roman/ui-serif) at a slightly larger size, matching the app's typographic distinction between what the user said and what the agent said. Tool calls and their results render as quiet monospace rows with a thin colored rule on the left edge — present, but visually subordinate to the prose, the same hierarchy the real chat view uses.
The composer itself — drawChatComposer — is drawn as a rounded pill with a placeholder ("Chat with Claude"), a model chip, a mic icon, and a white send disc, and it types out the pane's next prompt character by character using the same per-prompt characters-per-second rate the terminal version used for its typing animation. Even the empty-state treatment is matched: when a pane has no visible blocks yet, the renderer draws the same centered agent-mark-and-greeting empty state ("Good afternoon." over "Chat with Claude") that a genuinely empty native chat session shows, rather than leaving a blank rectangle.
Bottom-Up Layout, Not a Fixed Line Count
Because chat bubbles and serif paragraphs take up variable, wrapped height — unlike fixed-height terminal rows — the renderer lays out visible blocks bottom-up: it measures every currently-visible block's wrapped height, sums from the bottom until it exceeds the available space above the composer, and starts rendering from whichever block first fits. That's a real layout problem the terminal renderer never had, since monospace terminal rows are all the same height and just scroll a fixed line count.
The Honest Claim: Identical Content, Different Skin
The reason this rebuild is worth writing about isn't the visual polish — it's what reusing the script guarantees. Because PANE1 and PANE2 are untouched, the marketing demo cannot silently start showing the app doing something it doesn't actually do. Every prompt typed, every tool call named, every "he agent said" line in the demo is the exact same content that shipped in the terminal-rendered version, which was itself reviewed against what Influxx agents actually do. Rebuilding the renderer changed presentation: colors, typography, bubble shapes, composer chrome. It could not, by construction, change capability claims, because capability claims live in the script, and the script was never touched.
That's a meaningfully different guarantee than "we re-checked the new demo for accuracy." Re-checking a hand-written new script is a one-time audit that can go stale the next time someone edits it without re-auditing. Reusing the old script means the audit trail is the same one that already existed — there's no new set of claims to have gotten wrong.
"I watched the homepage demo before I signed up and genuinely couldn't tell if it was a real screen recording or not — it just looked like the app. When I actually opened Influxx for the first time, native chat looked exactly like what I'd seen on the site. That match mattered more to me than I expected; it's a small thing but it built trust before I'd typed a single prompt."
— Marcus Villareal, staff developer at a healthtech platform, Influxx user
The /demo-frame Dev Route
website/src/app/demo-frame/page.tsx exists purely for iterating on this renderer without paying the cost of the full scene. The production demo runs inside a WebGL-rendered 3D MacBook, which is slow to iterate against when you're tuning something like bubble padding or a wrap width. /demo-frame sidesteps that entirely: it draws the same drawInfluxxDemo function directly onto a plain 2D canvas, either as a single frame when a ?t= query parameter is given, or as a filmstrip of ten fixed timestamps (2s, 8s, 16s, 28s, 39s, 45s, 55s, 62s, 76s, 88s) spanning the whole 90-second script when it's not. Since the render function is a pure function of t, this dev-only page can call it directly and get pixel-identical output to what the WebGL scene would eventually composite onto the MacBook screen texture — just without the 3D scene, camera, or lighting around it.
That matters for a renderer with this much hand-tuned layout logic — text wrapping, bottom-up block placement, per-block color and font selection. Being able to jump straight to "what does the moment right after the agent starts its second tool call look like" without waiting on a WebGL scene to load is what makes iterating on those details practical.
"A product demo is a promise. The moment it shows a capability that isn't real, you've spent trust you don't get back easily. Rebuilding the renderer around the exact same script we'd already shipped and reviewed was the only way to get a better-looking demo without re-opening that promise. It's a small architectural choice — separate the script from the renderer — but it's the kind of choice that pays for itself the next time we want to reskin this again."
— Elena Voss, VP of Engineering at ETAPX
Why Rebuild the Skin At All
If the content didn't change, it's fair to ask what the rebuild actually bought. The answer is representational honesty in the other direction: showing a terminal was showing a real thing Influxx can do, but not the thing most users spend most of their time looking at. A homepage demo is implicitly a claim about what using the product feels like day to day, and a scripted TUI — however accurate to a real terminal session — was answering a slightly different question than "what does it look like when I talk to an agent in Influxx." Matching the native chat surface closes that gap without inventing new content to close it with.
Frequently Asked Questions
Did the actual prompts, tool calls, or timings in the demo change with this rebuild?
No. The PaneScript objects that define the demo's content are untouched — only the drawing functions that turn that script into pixels changed.
Is the marketing demo a video recording of the real app?
No. It's a scripted canvas animation that now uses the same color tokens, typography, and layout patterns as the real native chat surface, driven by a deterministic function of elapsed time rather than a captured recording.
Could someone edit the demo script to show a capability the app doesn't have?
Technically the script is still editable code, so nothing prevents a future edit from doing that. What this rebuild specifically guarantees is that the renderer change itself introduced no new content — the script that shipped after the rebuild is byte-for-byte the same script that was reviewed before it.
What is the /demo-frame route for, and is it available in production?
It's a dev-only page for previewing specific timeline frames of the demo as flat 2D canvases, without loading the full WebGL MacBook scene. It's meant for iterating on the renderer, not for end users.
Why does the demo use a serif font for assistant text but not for user messages?
That mirrors the real native chat surface's typographic hierarchy in the app, where assistant prose and user input are visually distinguished — the demo's renderer reads those same font choices from its token set rather than inventing its own style.
Does the demo still show a terminal anywhere?
No, the terminal-styled rendering path for these panes was replaced by the native-chat rendering path described here; the panes now draw as chat surfaces, not terminal panes.
Is the demo's layout hand-placed frame by frame, or computed?
Computed. Text wrapping, bubble sizing, and vertical placement are all derived at render time from the script content and the current timestamp — nothing is hardcoded to a specific frame.
The most convincing thing about a demo that shows real UI is what it refuses to do — invent new content just because the presentation got better.

