Build Genially
KINDAback to the verdict
An authoring tool for interactive presentations and infographics, where the interesting constraint is that interactivity authored in a visual editor usually only runs inside that editor's player, and content that cannot be exported or made accessible is content with an expiry date.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the editor and builds the player.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why Keyboard and announcement behaviour cannot be verified by reading code, and the failures are specific to how assistive technology interprets state changes.
Get it Your platform's screen reader, plus enough familiarity to navigate a page with it.
Verify
You can traverse a simple interactive page using only the screen reader - have readyfree
Why The reduced-motion and export reviews are about whether the content still communicates, and a demonstration of shapes has nothing to lose.
Get it Build something you would actually publish, with at least eight interactive elements and a point to make.
Verify
A piece exists with eight interactions and a stated purpose - have readyfree
Why Embeds break on real pages with real stylesheets, and testing against a blank page demonstrates nothing.
Get it A page with aggressive global styles, a restrictive content policy and other embedded content.
Verify
The embed is tested inside it and renders correctly - decidefree to read
Why Focus order, announcements and alternative text are enforced in the editor, and adding them after authoring means revisiting every piece.
Get it Pick the standard you will meet, write down what it requires of an interactive piece, and encode the checks.
Verify
A written standard exists and its checks are enforced at save
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- documents(id, title, canvas_size, created_at) - elements(id, document_id, kind, x, y, width, height, z, content_json, alt_text) - states(id, document_id, name, is_initial) · the states an interactive piece can be in - interactions(id, document_id, trigger_kind, source_element_id, from_state_id, to_state_id, effects_json) - focus_order(document_id, ordinal, element_id) · explicit, because derived order is usually wrong - exports(id, document_id, kind, path, includes_player, at) - An interaction is a transition between named states rather than a script attached to an element, which is what makes the whole piece testable and traversable by keyboard.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DOCUMENTS_DIR | required | ~/Documents/interactive | Authored pieces. A document, stored as one. |
EXPORT_DIR | required | ./exports | Self-contained bundles including the player. |
BUNDLE_PLAYER | required | always | Present as a constant. A hosted player is an expiry date on the content. |
REQUIRE_ALT_TEXT | required | always | Enforced at save for elements carrying meaning. |
MAX_ANIMATION_MS | required | 400 | A long transition is an unresponsive interface. |
INTERACTION_REPORTING | required | off | Off by default. Learners' interaction data is not collected without asking. |
The build, in order
Interactions as data, not as scripts
What a click does is described rather than programmed.
A script per element rules out everything the later phases need.
Files
src/interactions.tsAn unreachable layer is a mistake nobody finds by clicking.
Files
src/analyse.tssrc/analyse.test.tsThree implementations diverge and only one gets accessibility.
Files
src/interactions.ts
done when · tick each as it passeswatch out- Attaching a script to each element makes the piece impossible to traverse without executing it, which rules out keyboard navigation, screen reader support, testing and export in one decision. Describe transitions declaratively so the whole interaction graph can be walked.
Usable without a mouse
Every interaction can be reached and triggered by keyboard.
Derived order follows the document, not the meaning.
Files
src/focus.tsA silent change is invisible to anyone not watching the screen.
Files
src/announce.tsEnforced at save, not suggested in documentation.
Files
src/elements.ts
done when · tick each as it passeswatch out- Hotspots positioned visually give a focus order derived from the document that bears no relation to how the piece reads, so keyboard users traverse it in an order that makes no sense. Make the focus order explicit and part of the document rather than derived from element positions.
Animation that can be turned off
Motion is optional and the piece still communicates.
It is set for medical reasons rather than as a preference.
Files
src/motion.tsAssuming graceful degradation is how information disappears.
Files
src/review.tsA long transition is an unresponsive interface.
Files
src/motion.ts
done when · tick each as it passeswatch out- Building meaning into an animation, such as an arrow that only appears while a transition plays, means the piece loses information for anyone with reduced motion enabled, and that is a preference people set for medical reasons. Check the no-motion version explicitly rather than assuming it degrades gracefully.
A player small enough to ship
The piece runs from a self-contained export.
A hosted player is an expiry date on the content.
Files
src/export.tsIt ships with every export. Measure and state its size.
Files
player/From a file, with networking disabled.
Files
tests/export-offline.test.ts
done when · tick each as it passeswatch out- An export that references a hosted player means the content stops working when the account lapses or the platform changes its addresses, which for educational material used for years is a real loss. Bundle the player with the export and test it from a file with networking disabled.
Embedding and knowing it workedproduct builder
The piece works inside someone else's page and you can tell whether it did.
Inheriting the host stylesheet breaks it differently on every site.
Files
player/embed.tsIt is on someone else's page and must not slow it down.
Files
player/embed.tsLearners' interaction data is not collected by default.
Files
src/analytics.ts
done when · tick each as it passeswatch out- An embed that inherits the host page's stylesheet breaks unpredictably on every site it is placed on, and the author only finds out from whoever placed it. Isolate the embed's styling and test against a deliberately hostile host page rather than a blank one.
That is the whole plan for Genially. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Template and asset libraries.
- Real-time collaborative authoring.
- Learning management integration and grading.
- Motion design authoring.
- template marketplace, hosted embeds, analytics, collaboration, and education workflows
- huge template and asset libraries
- real-time multiplayer
- brand governance
- professional print workflows
- Branching scenarios, which the state machine already supports and which need authoring tools
- A preview mode that simulates keyboard-only use, which catches most accessibility problems
- Versioning of published pieces, so an update does not silently change what people already saw
Need the files? The project pack on the verdict page hands your agent the whole brief · more design.