Kyndium

Inside Timeline Studio's scene compiler

How a table of rows becomes a slide with real PowerPoint shapes, and why the renderer never touches React.

The Kyndium team3 min readengineeringtimeline studioexport

Timeline Studio draws a presentation timeline from a handful of rows and exports it to PowerPoint with every marker, label and spine as a native shape. The part that makes that possible is a small, deliberately boring piece of code we call the scene compiler.

Rows in, scene out

The compiler takes four things: the rows (a label, a date, maybe a caption, an icon and a color), a template, the settings a person changed, and any overrides they made by dragging or retyping on the stage. It returns a Scene: a flat list of typed primitives, rectangles, ellipses, lines, paths, text and icons, each with a stable id such as r5:label or spine:main.

Nothing in that step knows about the browser. The compiler lives in its own folder with a lint rule that bans importing React, Next.js, Supabase or the UI kit. It runs the same way in a test, on the server, or in the editor, which is why the test suite can compile a scene and write a real .pptx file in Node without a browser in sight.

Stable ids are the whole editing model

When you nudge a label on the stage, Studio does not move a shape. It records an override against the label's id: a few pixels of offset, or new text, or hidden. On the next compile the layout runs again from scratch and the override is applied at the end. That is why edits survive a template change: switch from Meridian to Cards and the row's label is still nudged, still reworded, because the id came from the row and the role, not from where the template happened to put it.

Comments work the same way. A comment is pinned to an element id, so it follows the element through re-layouts the way an override does.

Three renderers, one scene

The React stage, the SVG exporter and the PowerPoint exporter all consume the same Scene. The stage turns primitives into SVG elements with handles for selection and dragging. The SVG exporter writes the same primitives as a string, inlining the web fonts as data URLs so the file looks right off the network. PNG and PDF rasterize that SVG.

PowerPoint is the interesting one. A pure planning step maps each primitive onto a PowerPoint shape: rectangles and ellipses directly, chevrons and home plates for the process templates, parallelograms for Slant, a rotated teardrop for pins, and custom geometry with cubic curves for the serpentine spines. Text becomes text boxes with the size converted from pixels to points. Icons travel as SVG images. The plan is a plain description with no side effects, so it is tested like any other function; a second step applies the plan through pptxgenjs to produce the file. Gradients become a single color, which the export menu tells you.

Slides

Long timelines split into slides. The splitter divides rows into balanced runs under the template's capacity, and the compiler takes a slide index and count so a continued linear spine gets an arrow cap and a page number. The editor compiles one scene per slide; the PowerPoint export writes one slide per scene; the PDF one page; PNG and SVG one numbered file each.

Why bother

We could have drawn the timeline as one picture and exported a screenshot. The reason we didn't is the review meeting. A picture of a plan cannot be edited by the person presenting it at eleven at night. Shapes can. Keeping the compiler framework-agnostic is what made native shapes practical, and keeping ids stable is what made the result editable before and after export.

Was this useful?

Comments