Kynetic Editor: Instructions
Created by Hamd Waseem • hamdivazim/Kynetic
Self-hosting the editor website
The editor is a standard Next.js (App Router, client-heavy) application in editor/kynetic-editor. It stores projects in your browser and talks only to your own AWS API. There is no backend to deploy beyond the CDK stack below.
Requirements
Node.js 20 or newer and npm.
Run locally
cd editor/kynetic-editor npm install npm run dev # development server on http://localhost:3000
Build for production
npm run build npm start # serves the production build on port 3000
Deploy anywhere Node runs
The production build is self-contained. Run npm run build && npm start behind any reverse proxy, or deploy to a Node host such as a VPS, Railway, Render, Fly.io, or Vercel (import the repo and set the root directory to editor/kynetic-editor). No environment variables are required; the AWS API URL and API key are entered in the editor's AWS setup popup and stored in the browser's localStorage only.
Projects: nodes & transitions
A project is a JSON file (matching renderer/renderer/schema.py) with three parts: a scene (width, height, background color), definitions (the nodes), and a timeline (the transitions that animate them). The exported JSON is what the Docker renderer consumes.
Coordinates
The editor works in the renderer's world coordinates: the default 1920×1080 scene maps the world rectangle (0..1778, 0..1000) onto the video frame with a small margin, so the numbers you see in the inspector are exactly the numbers written to the JSON. Text/math nodes are positioned at their top-left, rectangle/square at their top-left corner, and circle/ellipse/ngon/glow-dot at their center.
Node types
| Node | Key fields | Info |
|---|---|---|
| math | tex_expression, font_size, font_name | LaTeX expression, e.g. $x^2$ |
| text | text, font_size | Plain text |
| square | side_length | position = top-left |
| rectangle | width, height | position = top-left |
| rounded_square / rounded_rectangle | side_length / width+height, border_radius | corner radius as a fraction of the short side |
| line | end_point | position = start point |
| polygon / curve / curved_arrow / arrow | points / start_point+end_point, arrow head style | arrows support ->, <-, <-> heads |
| ellipse | center, width, height | |
| circle / glow_dot / ngon | center, radius / +n | glow_dot renders as a glowing dot |
| svg | src, scale_x, scale_y | src is s3://key.svg (uploaded) or a path relative to the JSON |
| group | children_ids | children are defined before the group; the group's transition animates them together |
| eraser | objects_to_erase | whitens over previously defined nodes |
Styles
Every node can carry a stroke_style (color, width), an optional fill_style (hachured fill), an optional sketch_style (roughness/bowing/stroke multiplier) and a glow_dot_hint (a glowing dot that follows the pen while the node is drawn).
Transitions
| Transition | What it does | Best use cases |
|---|---|---|
| sketch | Draws the node on stroke-by-stroke (entrance) | Shapes, text, math, SVGs |
| fade_in | Fades the node in (entrance) | Labels and dots |
| fade_out | Fades the node out (exit) | Removing things |
| zoom_out | Shrinks the node to nothing (exit) | Animated exits |
| translate_to | Moves the node's center to the destination point; "stay at destination" keeps it there | Rearranging the scene |
Timeline rules
The renderer only shows a node once it has an entrance transition ( sketch or fade_in). The editor adds a default entrance whenever you create a node and fills in any missing ones on export, so nothing disappears. Transitions are exported sorted by start time; the video length is the end of the last transition plus one second (five seconds for a scene with no transitions). Each transition has a target node, a start time and a duration.
Preview, import and export
Preview plays the timeline in the browser using the same semantics as the renderer (entrance/exit visibility, translate-to-center, eraser whitening). Export JSON downloads the renderer-ready project; Import loads any project JSON back into the editor.
AWS setup with CDK (your own S3 bucket)
The AWS stack is optional: it gives you a private S3 bucket for SVG assets plus an API-Gateway/Lambda API that hands out presigned URLs. Everything runs in your own AWS account (BYOC); the editor stores the API URL and key only in your browser.
Deploy
# prerequisites: AWS CLI configured (aws configure), Node.js, Python 3.11+ npm install -g aws-cdk cd editor/cdk python -m venv .venv # Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate pip install -r requirements.txt cdk bootstrap # once per account/region cdk deploy
Full step-by-step detail (credentials, console navigation) is in editor/cdk/README.md. After deploying, note the API's invoke URL (API Gateway console → Kynetic-PresignedURL-API → Stages → prod) and the API key value (API Keys → ClientApiKey → Show).
Configure the editor
Open AWS setup in the editor header, paste the API URL (with or without the /prod stage - both work) and the API key, then use Test connection. SVG nodes can then upload to your bucket; the exported JSON references them as s3://filename.svg and the renderer fetches them at render time.
API routes
| Route | Consumer | Use |
|---|---|---|
| /get-url-frontend, /put-url-frontend | This editor | Get presigned URLs to upload/fetch remote SVGs from S3 from within the editor |
| /get-url, /put-url | The local renderer | Use by renderer to fetch SVGs from S3 |
All routes require the X-Api-Key header and are throttled by the usage plan; uploads and downloads go directly between the browser/renderer and S3 via short-lived presigned URLs.
Rendering with Docker
The renderer lives in renderer/ and is distributed as a Docker image (see renderer/README.md for the full guide). Export your project from the editor, then:
docker pull hamdivazim/kynetic-renderer:latest # project without remote assets docker run --rm \ -v "$(pwd)/<project_dir>:/input" -v "$(pwd)/<output_dir>:/output" \ hamdivazim/kynetic-renderer:latest /input/<project_file>.json # project using s3:// SVGs, pass your CDK API URL and key docker run --rm \ -e KYNETIC_API_URL="https://your-api-id.execute-api.region.amazonaws.com" \ -e KYNETIC_API_KEY="your-api-key" \ -v "$(pwd)/<project_dir>:/input" -v "$(pwd)/<output_dir>:/output" \ hamdivazim/kynetic-renderer:latest /input/<project_file>.json
KYNETIC_API_URL may be the bare API URL or the one ending in /prod; if the variables are omitted the container prompts for them. The video is written to the output directory next to the project file.
Keyboard shortcuts
| Shortcut | Action |
|---|---|
| Ctrl/⌘ + Z | Undo |
| Ctrl/⌘ + Shift + Z or Ctrl/⌘ + Y | Redo |
| Ctrl/⌘ + D | Duplicate the selected node(s) |
| Ctrl/⌘ + S | Export JSON |
| Delete / Backspace | Delete the selected node(s) |
| Escape | Deselect (or leave the focused field) |
| Shift + click | Add/remove a node from the selection; drag moves the whole selection |
| Mouse wheel | Zoom in/out around the cursor |
| Drag the canvas background | Pan the view |
| 0 or Home | Recenter to the original view |
| + / − | Zoom in/out around the view center |
Troubleshooting
SVG upload or preview fails with 403 / "Failed to fetch"
That is a CORS or authentication failure against your API. Check the API URL and key in AWS setup, make sure the key is the current ClientApiKey value (redeploying creates a new one), and redeploy the stack if it predates the current cdk_stack.py — the gateway now returns readable errors instead of opaque fetch failures. The editor uses the get-url-frontend / put-url-frontend routes.
The renderer cannot fetch an s3:// SVG
Confirm the file was uploaded (its name must match the src in the JSON exactly), and pass the same API URL/key via KYNETIC_API_URL / KYNETIC_API_KEY. The renderer uses the legacy get-url route; both URL forms (with and without /prod) are supported.
A node is missing from the rendered video
The renderer only shows nodes with an entrance transition. The editor guarantees one per node on export. If you hand-edit JSON, give every node at least one sketch or fade_in event.