Caveats
Things that behave differently from what you might expect, known limitations of this release, and
the workaround for each. Most of them are also caught by kinemo check; the diagnostic
code is given where one exists.
Build phase vs. playback#
- The scene function runs once. Python
if/fordecide the script at build time from values at the cursor (x.now). They never re-run during playback. To react while the video plays, usek.when,@event.onor expressions. x.nowis a snapshot;x()is a tracked read.x()in the scene body is K0301;x.nowinside a lambda is K0302 (it would freeze the value)..nowonly sees what is already scheduled. Animations started later in the script, and handlers (which run during resolve), are not visible to an earlier.nowors.wait_for.- Handlers and traced functions must be pure. The resolve phase may run a handler
several times until the event times stop changing;
kinemo devwarns when two builds of the same source differ. Global counters, the clock or unseeded randomness break this. - Event loops stop after 8 passes (K0501). A handler that always schedules the next
firing of its own event at a new instant never converges; use
once=Trueorrearm=.
Tracing#
- Functions passed to
.map,k.computedand lambdas in props are traced, not called per frame: they run once with symbolic values.if,and/or,math.*,int(), built-inmin()/max()on signals fail with K0310. Usek.where,&/|,k.sin,k.floor,k.min,k.max. k.python(fn)is the explicit escape hatch. It is precomputed once per frame at resolve time (the cost is shown bycheck) and cannot be exported to interactive web if it depends on a parameter (W1302).k.simulatestep functions and.onhandlers are plain Python:if,min,mathare fine there.- f-strings work in lambdas only with signals read as
x(); formatting a signal in the scene body without.nowis K0301.
Layout#
place()holds both axes.place(left_of=a)also aligns vertically (usealign=). Animatingx/yof a placed object is K0401: animate the constraint with.to_place(...), or release it with.to(..., unpin=True).- Children of containers are positioned by the container (
Row,Column,Grid,Stack). Reorder withswap/insert/pop/to(children=...); to nudge a child on top of the layout useblend="add". - Constraints use the unrotated bounding box unless
place(..., by="rotated"). - Scale and rotation pivot on the bounding-box center of the object (prop
anchor). A group's pivot therefore moves when its children change. - One parent per object (K0103). Use
obj.copy()for a second instance, ork.reparent(obj, group)to move it (instant; the world position is kept). - Hidden children (
visible=False) do not count in their group's bounding box; transparent ones (opacity=0) do. tri.sidesandSquare.on(side)are evaluated at the cursor: the squares do not follow the triangle if it moves later.
Animations#
- Two animations on the same prop at the same time are an error (K0201), including
hidden overlaps such as a
duringrevert meeting a later.to(). Sequence them or useblend="add"for intentional sums. s.play(..., at=)does not move the cursor (W0110); writes.start(..., at=).duration=rescales the whole composition, keeping the relative timing of its parts.s.duringonly accepts reversible animations (.to(),k.indicate); entry and exit verbs are K0204.- Progressive tempo (
s.tempo(1, to=8)) is applied when the block ends; inside the block,.nowon animations started before the block reads slightly different times. - Clips with
duration=run twice (once to measure, then for real). Side effects outside kinemo inside a clip happen twice. - Lists only interpolate when declared (
k.signal([...], lerp=k.lerp.pointwise)), otherwise K0205.
Text, math and code#
- Markup is minimal:
**bold**,*italic*,`code`. Bold italic renders as bold.$...$insidek.Textis kept literally — usek.Mathfor formulas. - Text layout is left-to-right only (no bidirectional text) and uses the bundled DejaVu fonts; system fonts are never used, so output is identical on every machine.
txt["word"]addresses the first occurrence in the text without markup; usetxt.find_all("word")for all of them.k.Mathcovers mathematical LaTeX, not text-mode or TikZ commands (K0801).\textcolor/\colorare ignored (style parts witheq["..."].to(color=...)instead),\tag/\labelrender nothing,\hspaceis always 0.5 em,arraycolumn specs use the first column's alignment.engine="tex"is not available yet.- Subexpression lookup matches whole syntax-tree nodes: in
a^2 + b^2 = c^2,a^2andc^2are nodes buta^2 + b^2is not unless wrapped in{}or\id{...}{...}. - Code highlighting has no language injections (SQL inside strings stays a string); repeated identical lines may pair unexpectedly in a code morph.
Charts and data#
ax.plotsamples the function at build time with floats; the curve then followsuntil=/from_=signals and zooms natively. Curves are cut at the visible x and y ranges — choosey=to cover the values you want to see.- Ticks are generated for the initial ranges; after a zoom, ticks outside the new range hide, but new tick values are not created.
LineChart.to(data=)keeps they=column names; rename series by keeping column names stable across datasets.- Data must be Arrow-compatible (polars, pandas ≥ 2.2, pyarrow, duckdb), numpy, or plain Python lists/dicts; anything else is K1201.
Rendering and output#
- The CPU renderer (tiny-skia) is the reference. The optional GPU backend
(
maturin develop --features gpu) is only used by thedevpreview and is usually not faster for typical scenes. - MP4 drops transparency; use
--format webm --transparentor--format mov. - Odd output sizes are padded by one pixel in MP4 (yuv420p needs even dimensions).
- Slides split audio at section boundaries: a narration starting before a slide break is not carried into the next section.
--format web(interactive) and 3D (camera="3d",s.camera,s.hud) are not in this release; the spec plans them for later versions.- Voice without a TTS provider is silence with a duration estimated at 150 words/min
(W1401). Install a provider package and set it in
kinemo.toml.
Editing from the preview#
- Only literals are editable. A value that comes from a variable, an expression or a function argument is shown read-only; edit it in the code. The preview edits arguments that already exist (or adds a keyword for a default prop); it never adds statements.
- Editing a line inside a loop changes every run of it (the
×Nmark). A clip's body is one line for all its uses. - Dragging an object adds the pointer distance to its literal position. Inside a scaled
or rotated group the object moves by a different amount on screen than the pointer.
Objects positioned by a relation (
place(above=...)), a container or an expression can't be dragged, and neither can an object whosex/yis animating at the playhead. - An edit is refused when the file changed since the preview last built it (for example unsaved changes in the editor that were then saved); the preview reloads and the edit can be repeated.
Not yet implemented#
These are described in the specification but are not implemented yet:
k.morph_cutrenders as a crossfade; objects with the samekey=do not travel across the cut yet.kinemo dev --debugdrawslayoutandsafe; themorphandtraceoverlays, and the events track on the timeline, are not there yet.s.start(..., loop=True)(looping animations that keep running on a paused slide).$...$inline math insidek.Text; use a separatek.Math.kinemo checkdoes not yet print the measured cost of eachk.pythonfunction.k.PolarAxes.plotis sampled at build time: it does not follow later changes ofr_max/radius.
Typing#
- Component prop defaults must use
k.prop(...)to type-check in strict mode:power: k.Prop[float] = k.prop(0.0). A bare= 0.0works at runtime but Pyright rejects it. - Simulation fields (
sim.y) are typedAny: Pyright cannot map ak.State's fields onto the simulation object. - User component constructors accept
**kwargs: object, not per-prop types.
Performance#
- Avoid thousands of individual objects (W0901): use
k.Points,k.VectorFieldork.StreamLines, which are drawn in batches. k.pythonfunctions run once per frame of the scene during resolve; vectorize them (k.python(fn, vectorized=True)) for long scenes.- The build uses about 10 GB of
target/in a full development checkout (typst, wgpu and tree-sitter grammars).cargo buildexcludes the GPU crate by default.