From static slides to a postable video: finishing my LINE-style chat generator with GitHub Copilot
From static slides to a postable video: finishing my LINE-style chat generator with GitHub Copilot
What I Built
A while back I built a small web tool that turns a block of conversation text into a vertical (9:16), iPhone-style LINE chat slide — with the message bubbles animating in one by one. It was made for talks: paste a conversation, download an animated
.pptx, present it. I even wrote it up here on DEV.But it always had a missing half. A
.pptx is great on a projector and awkward everywhere else — you can't really post it. What I actually wanted, and never finished, was to turn the same conversation into a video I could drop into a social post, where the bubbles pop in like a chat happening live.This challenge was the push to finish that. The app now exports the conversation as a vertical mp4 (and webm), with bubbles fading in one at a time and a "typing…" indicator before each incoming message — all in the browser, no server involved.
Demo
- App: https://line-chat-app-nine.vercel.app
- Repo: https://github.com/yama3133/line-chat-app

The Comeback Story
Where it was
- pptx generation only (python-pptx + hand-written animation XML), already shipped and documented.
- There was an animated preview in the browser, but the only thing you could save was a pptx.
- The "export it as a video" idea sat untouched for months.
What I added
- Rebuilt the LINE / iPhone UI on a
<canvas>. The pptx version draws with python-pptx shapes and the old browser preview was CSS — neither can be recorded as a video, so the UI had to be redrawn on canvas. - Accurate bubble wrapping with
ctx.measureText, instead of the character-width guess the python version had to rely on. - The one-by-one fade-in animation, driven by a single
renderFrame(ctx, opts, t)that draws the exact state at timet(the same function is reused for recording). - Recording with
MediaRecorder. I expected to need ffmpeg.wasm for mp4 — but Chrome's MediaRecorder supportsvideo/mp4;codecs=avc1.42E01E(H.264) directly, so mp4 comes out with no extra dependency. - A reliability fix that surprised me: a naive
requestAnimationFramerecord loop silently stalls when the tab isn't focused. I switched tosetTimeout+canvas.captureStream(0)+track.requestFrame(), so recording keeps going even in a background tab. - A "typing…" indicator before each incoming bubble, for a more chat-like feel.
Still on the list
- GIF export (handy for Slack / LINE) via ffmpeg.wasm or gif.js.
My Experience with GitHub Copilot
I leaned on Copilot for the part that's tedious but well-defined: drawing the LINE / iPhone UI on canvas. The approach was comment-driven — I wrote each function's spec as a comment first, then let Copilot fill in the body:
1
2
3
// Draw the phone frame (black, rounded) filling the logical area, then the
// inner screen (chat background) inset by L.phone.pad. Return {x, y, w, h}.
function drawPhoneFrame(ctx) { /* Copilot */ }Where it shone:
- Repetitive shape code — the 4 signal bars, the 3-dot menu, the battery with its little nub — came out correct from a one-line comment.
- It picked up my existing constants (
Vfor colors,Lfor layout) and reused them instead of hardcoding values. - Asking it to implement all the
TODO(Copilot)functions at once filled the whole UI layer in a single pass.
Where I took over: the bubble-wrapping math, the animation timing, and the background-recording fix were things I designed and wrote by hand.
The split that worked for me: Copilot builds the UI scaffolding fast; I own the logic. My one practical tip is to treat comments as the spec — the more precise the comment, the better the completion.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article