Vibecode Genially
track this build4 phases, 12 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Genially, build interactive presentations with hotspots, layers, and simple animations. The hard boundary is template marketplace, hosted embeds, analytics, collaboration, and education workflows, plus editor polish, assets, collaboration, and distribution.
You are building a lean indie version of Genially. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Genially · indie build 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. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Model | Interactions as a declarative state machine over elements | an interaction described as data can be exported, tested and made accessible | | Runtime | A small player consuming the declaration | the player is what makes exported content work elsewhere | | Accessibility | Keyboard and screen reader paths derived from the same declaration | hotspots that only respond to a mouse exclude people entirely | | Export | Self-contained output including the player | content locked to a hosted viewer disappears when the account does | | Runtime stack | TypeScript with local documents | authored content is a document | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the editor and builds the player. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A screen reader you can actually use** · free - 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 - [ ] **A real interactive piece with a purpose** · free - 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 - [ ] **A deliberately hostile host page** · free - 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 - [ ] **Your accessibility standard, chosen up front** · free 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 ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - 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 If one of those is essential to you, that is the reason to keep paying for Genially, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Genially The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me interactive content that still works when it leaves your platform to replace Genially. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Model: Interactions as a declarative state machine over elements · an interaction described as data can be exported, tested and made accessible - Runtime: A small player consuming the declaration · the player is what makes exported content work elsewhere - Accessibility: Keyboard and screen reader paths derived from the same declaration · hotspots that only respond to a mouse exclude people entirely - Export: Self-contained output including the player · content locked to a hosted viewer disappears when the account does - Runtime stack: TypeScript with local documents · authored content is a document ### Data model (create this before Phase 1) - 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. ### Phase 1 · Interactions as data, not as scripts Build: Model interactions as transitions between named states with declared effects. Support hotspots, layers and reveals through the same mechanism. Detect unreachable states and transitions that lead nowhere. Trap: 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. Done when: Interactions are stored as declarative transitions and the full interaction graph can be walked without executing anything. Unreachable states are detected and reported. A transition to a nonexistent state is impossible. The same mechanism handles hotspots, layers and reveals rather than three separate implementations. Do not build yet: No accessibility, player or export. ### Phase 2 · Usable without a mouse Build: Derive keyboard navigation from the interaction graph, with an explicit focus order. Announce state changes to assistive technology. Require alternative text for any element carrying meaning. Trap: 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. Done when: Every interactive element is reachable and triggerable by keyboard in an explicit order. A state change is announced to assistive technology rather than changing silently. An element carrying meaning cannot be saved without alternative text. The piece is navigable end to end with a screen reader, tested rather than assumed. Do not build yet: No player, export or animation yet. ### Phase 3 · Animation that can be turned off Build: Declarative animations on transitions, honouring the reduced-motion preference by replacing them with immediate changes. Verify the piece is comprehensible with all motion disabled. Trap: 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. Done when: Reduced motion replaces animations with immediate state changes throughout. The piece is reviewed with all motion disabled and the review is recorded. Content whose meaning depends on an animation is flagged. Animation duration is capped so a transition cannot block interaction for long. Do not build yet: No player or export yet. ### Phase 4 · A player small enough to ship Build: A player consuming the declaration, small and dependency free. Export a self-contained bundle including the player, the declaration and the assets. Verify the export runs from a local file with no network. Trap: 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. Done when: An exported bundle runs from a local file with networking disabled. The player is small enough to ship with every export, with its size measured and stated. Assets are embedded or bundled rather than referenced remotely. The exported piece keeps keyboard navigation and reduced-motion behaviour. Do not build yet: No embedding or analytics yet. ### Phase 5 · Embedding and knowing it worked Build: An embed that isolates from the host page's styles and does not block its loading. Report basic interaction completion where the author enables it. Handle a host page with conflicting styles and a restrictive content policy. Trap: 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. Done when: The embed renders correctly inside a page with deliberately conflicting styles. It loads asynchronously without blocking the host page. It functions under a restrictive content policy or fails with a clear message. Interaction completion reporting is off by default and states what it records when enabled. Do not build yet: Nothing after this. ### Out of scope (and why) - Template and asset libraries, which is most of what the hosted products actually sell. - Real-time collaborative authoring, which is a synchronisation project rather than an authoring one. - Learning management integration and grading, which is a standards-heavy field of its own. - Rich animation authoring comparable to a motion tool, which is a different discipline entirely. ### README must contain - That interactions are declarative transitions, which is what makes keyboard navigation, testing and export possible at all - That focus order is explicit rather than derived, because derived order follows the document rather than the meaning - That reduced motion is honoured and the no-motion version is reviewed rather than assumed to degrade gracefully - That the player is bundled with every export, so content does not expire with an account - That interaction reporting is off by default and states what it records ===== AGENTS.md ===== # Agent instructions · Genially indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Interactions as a declarative state machine over elements, A small player consuming the declaration, Keyboard and screen reader paths derived from the same declaration, Self-contained output including the player, TypeScript with local documents. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - 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. - 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. - 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. - 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. ===== BUILD_PLAN.md ===== # Build plan · Genially 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. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Interactions as data, not as scripts What a click does is described rather than programmed. ### Steps 1. Model transitions between named states A script per element rules out everything the later phases need. Files: `src/interactions.ts` 2. Walk the graph for reachability An unreachable layer is a mistake nobody finds by clicking. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Use one mechanism for all interaction kinds Three implementations diverge and only one gets accessibility. Files: `src/interactions.ts` ### Done when - [ ] The interaction graph walks without execution - [ ] Unreachable states are reported - [ ] Transitions to nonexistent states are impossible - [ ] One mechanism covers hotspots, layers and reveals ### Watch 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. ## Phase 2 · Usable without a mouse Every interaction can be reached and triggered by keyboard. ### Steps 1. Make focus order explicit Derived order follows the document, not the meaning. Files: `src/focus.ts` 2. Announce state changes A silent change is invisible to anyone not watching the screen. Files: `src/announce.ts` 3. Require alternative text for meaningful elements Enforced at save, not suggested in documentation. Files: `src/elements.ts` ### Done when - [ ] Every interaction is keyboard reachable in an explicit order - [ ] State changes are announced - [ ] Meaningful elements require alternative text - [ ] A screen reader traverses the piece end to end ### Watch 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. ## Phase 3 · Animation that can be turned off Motion is optional and the piece still communicates. ### Steps 1. Honour reduced motion everywhere It is set for medical reasons rather than as a preference. Files: `src/motion.ts` 2. Review the no-motion version Assuming graceful degradation is how information disappears. Files: `src/review.ts` 3. Cap animation duration A long transition is an unresponsive interface. Files: `src/motion.ts` ### Done when - [ ] Reduced motion replaces animation with immediate changes - [ ] The no-motion version is reviewed and recorded - [ ] Motion-dependent meaning is flagged - [ ] Animation duration is capped ### Watch 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. ## Phase 4 · A player small enough to ship The piece runs from a self-contained export. ### Steps 1. Bundle the player with the export A hosted player is an expiry date on the content. Files: `src/export.ts` 2. Keep the player small It ships with every export. Measure and state its size. Files: `player/` 3. Test the export offline From a file, with networking disabled. Files: `tests/export-offline.test.ts` ### Done when - [ ] An export runs from a local file offline - [ ] The player size is measured and stated - [ ] Assets are bundled rather than referenced - [ ] Accessibility survives the export ### Watch 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. ## Not in this build - Template and asset libraries. - Real-time collaborative authoring. - Learning management integration and grading. - Motion design authoring. ## After v1, if you want it - 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 ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Authored pieces. A document, stored as one. DOCUMENTS_DIR=~/Documents/interactive # Required. Self-contained bundles including the player. EXPORT_DIR=./exports # Required. Present as a constant. A hosted player is an expiry date on the content. BUNDLE_PLAYER=always # Required. Enforced at save for elements carrying meaning. REQUIRE_ALT_TEXT=always # Required. A long transition is an unresponsive interface. MAX_ANIMATION_MS=400 # Required. Off by default. Learners' interaction data is not collected without asking. INTERACTION_REPORTING=off
You are building a lean indie version of Genially. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Genially · indie build 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. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Model | Interactions as a declarative state machine over elements | an interaction described as data can be exported, tested and made accessible | | Runtime | A small player consuming the declaration | the player is what makes exported content work elsewhere | | Accessibility | Keyboard and screen reader paths derived from the same declaration | hotspots that only respond to a mouse exclude people entirely | | Export | Self-contained output including the player | content locked to a hosted viewer disappears when the account does | | Runtime stack | TypeScript with local documents | authored content is a document | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the editor and builds the player. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A screen reader you can actually use** · free - 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 - [ ] **A real interactive piece with a purpose** · free - 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 - [ ] **A deliberately hostile host page** · free - 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 - [ ] **Your accessibility standard, chosen up front** · free 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 ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - 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 If one of those is essential to you, that is the reason to keep paying for Genially, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Genially The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me interactive content that still works when it leaves your platform to replace Genially. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Model: Interactions as a declarative state machine over elements · an interaction described as data can be exported, tested and made accessible - Runtime: A small player consuming the declaration · the player is what makes exported content work elsewhere - Accessibility: Keyboard and screen reader paths derived from the same declaration · hotspots that only respond to a mouse exclude people entirely - Export: Self-contained output including the player · content locked to a hosted viewer disappears when the account does - Runtime stack: TypeScript with local documents · authored content is a document ### Data model (create this before Phase 1) - 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. ### Phase 1 · Interactions as data, not as scripts Build: Model interactions as transitions between named states with declared effects. Support hotspots, layers and reveals through the same mechanism. Detect unreachable states and transitions that lead nowhere. Trap: 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. Done when: Interactions are stored as declarative transitions and the full interaction graph can be walked without executing anything. Unreachable states are detected and reported. A transition to a nonexistent state is impossible. The same mechanism handles hotspots, layers and reveals rather than three separate implementations. Do not build yet: No accessibility, player or export. ### Phase 2 · Usable without a mouse Build: Derive keyboard navigation from the interaction graph, with an explicit focus order. Announce state changes to assistive technology. Require alternative text for any element carrying meaning. Trap: 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. Done when: Every interactive element is reachable and triggerable by keyboard in an explicit order. A state change is announced to assistive technology rather than changing silently. An element carrying meaning cannot be saved without alternative text. The piece is navigable end to end with a screen reader, tested rather than assumed. Do not build yet: No player, export or animation yet. ### Phase 3 · Animation that can be turned off Build: Declarative animations on transitions, honouring the reduced-motion preference by replacing them with immediate changes. Verify the piece is comprehensible with all motion disabled. Trap: 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. Done when: Reduced motion replaces animations with immediate state changes throughout. The piece is reviewed with all motion disabled and the review is recorded. Content whose meaning depends on an animation is flagged. Animation duration is capped so a transition cannot block interaction for long. Do not build yet: No player or export yet. ### Phase 4 · A player small enough to ship Build: A player consuming the declaration, small and dependency free. Export a self-contained bundle including the player, the declaration and the assets. Verify the export runs from a local file with no network. Trap: 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. Done when: An exported bundle runs from a local file with networking disabled. The player is small enough to ship with every export, with its size measured and stated. Assets are embedded or bundled rather than referenced remotely. The exported piece keeps keyboard navigation and reduced-motion behaviour. Do not build yet: No embedding or analytics yet. ### Phase 5 · Embedding and knowing it worked Build: An embed that isolates from the host page's styles and does not block its loading. Report basic interaction completion where the author enables it. Handle a host page with conflicting styles and a restrictive content policy. Trap: 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. Done when: The embed renders correctly inside a page with deliberately conflicting styles. It loads asynchronously without blocking the host page. It functions under a restrictive content policy or fails with a clear message. Interaction completion reporting is off by default and states what it records when enabled. Do not build yet: Nothing after this. ### Out of scope (and why) - Template and asset libraries, which is most of what the hosted products actually sell. - Real-time collaborative authoring, which is a synchronisation project rather than an authoring one. - Learning management integration and grading, which is a standards-heavy field of its own. - Rich animation authoring comparable to a motion tool, which is a different discipline entirely. ### README must contain - That interactions are declarative transitions, which is what makes keyboard navigation, testing and export possible at all - That focus order is explicit rather than derived, because derived order follows the document rather than the meaning - That reduced motion is honoured and the no-motion version is reviewed rather than assumed to degrade gracefully - That the player is bundled with every export, so content does not expire with an account - That interaction reporting is off by default and states what it records ===== AGENTS.md ===== # Agent instructions · Genially indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Interactions as a declarative state machine over elements, A small player consuming the declaration, Keyboard and screen reader paths derived from the same declaration, Self-contained output including the player, TypeScript with local documents. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - 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. - 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. - 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. - 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. ===== BUILD_PLAN.md ===== # Build plan · Genially 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. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Interactions as data, not as scripts What a click does is described rather than programmed. ### Steps 1. Model transitions between named states A script per element rules out everything the later phases need. Files: `src/interactions.ts` 2. Walk the graph for reachability An unreachable layer is a mistake nobody finds by clicking. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Use one mechanism for all interaction kinds Three implementations diverge and only one gets accessibility. Files: `src/interactions.ts` ### Done when - [ ] The interaction graph walks without execution - [ ] Unreachable states are reported - [ ] Transitions to nonexistent states are impossible - [ ] One mechanism covers hotspots, layers and reveals ### Watch 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. ## Phase 2 · Usable without a mouse Every interaction can be reached and triggered by keyboard. ### Steps 1. Make focus order explicit Derived order follows the document, not the meaning. Files: `src/focus.ts` 2. Announce state changes A silent change is invisible to anyone not watching the screen. Files: `src/announce.ts` 3. Require alternative text for meaningful elements Enforced at save, not suggested in documentation. Files: `src/elements.ts` ### Done when - [ ] Every interaction is keyboard reachable in an explicit order - [ ] State changes are announced - [ ] Meaningful elements require alternative text - [ ] A screen reader traverses the piece end to end ### Watch 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. ## Phase 3 · Animation that can be turned off Motion is optional and the piece still communicates. ### Steps 1. Honour reduced motion everywhere It is set for medical reasons rather than as a preference. Files: `src/motion.ts` 2. Review the no-motion version Assuming graceful degradation is how information disappears. Files: `src/review.ts` 3. Cap animation duration A long transition is an unresponsive interface. Files: `src/motion.ts` ### Done when - [ ] Reduced motion replaces animation with immediate changes - [ ] The no-motion version is reviewed and recorded - [ ] Motion-dependent meaning is flagged - [ ] Animation duration is capped ### Watch 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. ## Phase 4 · A player small enough to ship The piece runs from a self-contained export. ### Steps 1. Bundle the player with the export A hosted player is an expiry date on the content. Files: `src/export.ts` 2. Keep the player small It ships with every export. Measure and state its size. Files: `player/` 3. Test the export offline From a file, with networking disabled. Files: `tests/export-offline.test.ts` ### Done when - [ ] An export runs from a local file offline - [ ] The player size is measured and stated - [ ] Assets are bundled rather than referenced - [ ] Accessibility survives the export ### Watch 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. ## Not in this build - Template and asset libraries. - Real-time collaborative authoring. - Learning management integration and grading. - Motion design authoring. ## After v1, if you want it - 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 ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Authored pieces. A document, stored as one. DOCUMENTS_DIR=~/Documents/interactive # Required. Self-contained bundles including the player. EXPORT_DIR=./exports # Required. Present as a constant. A hosted player is an expiry date on the content. BUNDLE_PLAYER=always # Required. Enforced at save for elements carrying meaning. REQUIRE_ALT_TEXT=always # Required. A long transition is an unresponsive interface. MAX_ANIMATION_MS=400 # Required. Off by default. Learners' interaction data is not collected without asking. INTERACTION_REPORTING=off
You are building a production product version of Genially. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Genially · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Genially, build interactive presentations with hotspots, layers, and simple animations. The hard boundary is template marketplace, hosted embeds, analytics, collaboration, and education workflows, plus editor polish, assets, collaboration, and distribution. ## Product outcome Interactive content that can be navigated with a keyboard, understood with motion disabled, and still works years later from an exported file with no platform behind it. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - modern desktop browser - local fonts and images - optional image-generation API key ## Explicit non-goals for v1 - 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 ## Success criteria - The interaction graph is confirmed walkable without execution - Every interaction is confirmed keyboard reachable in an explicit order - The no-motion version is confirmed reviewed - An export is confirmed to run offline from a local file - The embed is confirmed to survive conflicting host styles ===== BRIEF.md ===== # Build brief · Genially The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me interactive content that still works when it leaves your platform to replace Genially. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Model: Interactions as a declarative state machine over elements · an interaction described as data can be exported, tested and made accessible - Runtime: A small player consuming the declaration · the player is what makes exported content work elsewhere - Accessibility: Keyboard and screen reader paths derived from the same declaration · hotspots that only respond to a mouse exclude people entirely - Export: Self-contained output including the player · content locked to a hosted viewer disappears when the account does - Runtime stack: TypeScript with local documents · authored content is a document ### Data model (create this before Phase 1) - 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. ### Phase 1 · Interactions as data, not as scripts Build: Model interactions as transitions between named states with declared effects. Support hotspots, layers and reveals through the same mechanism. Detect unreachable states and transitions that lead nowhere. Trap: 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. Done when: Interactions are stored as declarative transitions and the full interaction graph can be walked without executing anything. Unreachable states are detected and reported. A transition to a nonexistent state is impossible. The same mechanism handles hotspots, layers and reveals rather than three separate implementations. Do not build yet: No accessibility, player or export. ### Phase 2 · Usable without a mouse Build: Derive keyboard navigation from the interaction graph, with an explicit focus order. Announce state changes to assistive technology. Require alternative text for any element carrying meaning. Trap: 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. Done when: Every interactive element is reachable and triggerable by keyboard in an explicit order. A state change is announced to assistive technology rather than changing silently. An element carrying meaning cannot be saved without alternative text. The piece is navigable end to end with a screen reader, tested rather than assumed. Do not build yet: No player, export or animation yet. ### Phase 3 · Animation that can be turned off Build: Declarative animations on transitions, honouring the reduced-motion preference by replacing them with immediate changes. Verify the piece is comprehensible with all motion disabled. Trap: 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. Done when: Reduced motion replaces animations with immediate state changes throughout. The piece is reviewed with all motion disabled and the review is recorded. Content whose meaning depends on an animation is flagged. Animation duration is capped so a transition cannot block interaction for long. Do not build yet: No player or export yet. ### Phase 4 · A player small enough to ship Build: A player consuming the declaration, small and dependency free. Export a self-contained bundle including the player, the declaration and the assets. Verify the export runs from a local file with no network. Trap: 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. Done when: An exported bundle runs from a local file with networking disabled. The player is small enough to ship with every export, with its size measured and stated. Assets are embedded or bundled rather than referenced remotely. The exported piece keeps keyboard navigation and reduced-motion behaviour. Do not build yet: No embedding or analytics yet. ### Phase 5 · Embedding and knowing it worked Build: An embed that isolates from the host page's styles and does not block its loading. Report basic interaction completion where the author enables it. Handle a host page with conflicting styles and a restrictive content policy. Trap: 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. Done when: The embed renders correctly inside a page with deliberately conflicting styles. It loads asynchronously without blocking the host page. It functions under a restrictive content policy or fails with a clear message. Interaction completion reporting is off by default and states what it records when enabled. Do not build yet: Nothing after this. ### Out of scope (and why) - Template and asset libraries, which is most of what the hosted products actually sell. - Real-time collaborative authoring, which is a synchronisation project rather than an authoring one. - Learning management integration and grading, which is a standards-heavy field of its own. - Rich animation authoring comparable to a motion tool, which is a different discipline entirely. ### README must contain - That interactions are declarative transitions, which is what makes keyboard navigation, testing and export possible at all - That focus order is explicit rather than derived, because derived order follows the document rather than the meaning - That reduced motion is honoured and the no-motion version is reviewed rather than assumed to degrade gracefully - That the player is bundled with every export, so content does not expire with an account - That interaction reporting is off by default and states what it records ===== ARCHITECTURE.md ===== # Architecture · Genially ## Stack | Part | Choice | Why | | --- | --- | --- | | Model | Interactions as a declarative state machine over elements | an interaction described as data can be exported, tested and made accessible | | Runtime | A small player consuming the declaration | the player is what makes exported content work elsewhere | | Accessibility | Keyboard and screen reader paths derived from the same declaration | hotspots that only respond to a mouse exclude people entirely | | Export | Self-contained output including the player | content locked to a hosted viewer disappears when the account does | | Runtime stack | TypeScript with local documents | authored content is a document | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Interactions | Declarative state transitions. | Not swappable for per-element scripts, which rule out every later phase. | | Accessibility | Explicit focus order and announcements. | Any implementation, provided focus order is authored rather than derived. | | Motion | Optional animation with a reviewed alternative. | Any implementation, provided the no-motion version is checked. | | Player | A small bundled runtime. | Any player, provided it ships with the export. | | Embed | Style isolation and asynchronous loading. | Any technique, provided it survives a hostile host page. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DOCUMENTS_DIR` · required · Authored pieces. A document, stored as one. - `EXPORT_DIR` · required · Self-contained bundles including the player. - `BUNDLE_PLAYER` · required · Present as a constant. A hosted player is an expiry date on the content. - `REQUIRE_ALT_TEXT` · required · Enforced at save for elements carrying meaning. - `MAX_ANIMATION_MS` · required · A long transition is an unresponsive interface. - `INTERACTION_REPORTING` · required · Off by default. Learners' interaction data is not collected without asking. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Genially product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Interactions as a declarative state machine over elements, A small player consuming the declaration, Keyboard and screen reader paths derived from the same declaration, Self-contained output including the player, TypeScript with local documents. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - 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. - 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. - 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. - 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. - 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. ===== MILESTONES.md ===== # Delivery milestones · Genially Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Interactions as data, not as scripts What a click does is described rather than programmed. ### Steps 1. Model transitions between named states A script per element rules out everything the later phases need. Files: `src/interactions.ts` 2. Walk the graph for reachability An unreachable layer is a mistake nobody finds by clicking. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Use one mechanism for all interaction kinds Three implementations diverge and only one gets accessibility. Files: `src/interactions.ts` ### Done when - [ ] The interaction graph walks without execution - [ ] Unreachable states are reported - [ ] Transitions to nonexistent states are impossible - [ ] One mechanism covers hotspots, layers and reveals ### Watch 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. ## M2 · Usable without a mouse Every interaction can be reached and triggered by keyboard. ### Steps 1. Make focus order explicit Derived order follows the document, not the meaning. Files: `src/focus.ts` 2. Announce state changes A silent change is invisible to anyone not watching the screen. Files: `src/announce.ts` 3. Require alternative text for meaningful elements Enforced at save, not suggested in documentation. Files: `src/elements.ts` ### Done when - [ ] Every interaction is keyboard reachable in an explicit order - [ ] State changes are announced - [ ] Meaningful elements require alternative text - [ ] A screen reader traverses the piece end to end ### Watch 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. ## M3 · Animation that can be turned off Motion is optional and the piece still communicates. ### Steps 1. Honour reduced motion everywhere It is set for medical reasons rather than as a preference. Files: `src/motion.ts` 2. Review the no-motion version Assuming graceful degradation is how information disappears. Files: `src/review.ts` 3. Cap animation duration A long transition is an unresponsive interface. Files: `src/motion.ts` ### Done when - [ ] Reduced motion replaces animation with immediate changes - [ ] The no-motion version is reviewed and recorded - [ ] Motion-dependent meaning is flagged - [ ] Animation duration is capped ### Watch 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. ## M4 · A player small enough to ship The piece runs from a self-contained export. ### Steps 1. Bundle the player with the export A hosted player is an expiry date on the content. Files: `src/export.ts` 2. Keep the player small It ships with every export. Measure and state its size. Files: `player/` 3. Test the export offline From a file, with networking disabled. Files: `tests/export-offline.test.ts` ### Done when - [ ] An export runs from a local file offline - [ ] The player size is measured and stated - [ ] Assets are bundled rather than referenced - [ ] Accessibility survives the export ### Watch 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. ## M5 · Embedding and knowing it worked (production only) The piece works inside someone else's page and you can tell whether it did. ### Steps 1. Isolate the embed's styling Inheriting the host stylesheet breaks it differently on every site. Files: `player/embed.ts` 2. Load asynchronously It is on someone else's page and must not slow it down. Files: `player/embed.ts` 3. Make reporting opt in and explicit Learners' interaction data is not collected by default. Files: `src/analytics.ts` ### Done when - [ ] The embed survives conflicting host styles - [ ] It loads without blocking the host page - [ ] A restrictive policy fails clearly - [ ] Reporting is off by default and states what it records ### Watch 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. ===== OPERATIONS.md ===== # Operations · Genially ## Backup Authored documents are the work and exports are derivable from them. Back up the documents and the assets, and keep at least one export as a check that the pipeline still reproduces what you published. ## Restore Restore and confirm exports still reproduce identically, since a difference means the player version changed and previously published content may behave differently from a fresh export of the same document. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For an authoring tool the checks live in the editor: unreachable states, missing alternative text and motion-dependent meaning are all caught before publication, which is the only useful moment. ## Incident checklist Published content that stops working is the failure this build exists to prevent, and it happens when something was referenced rather than bundled. The offline export test is the control. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] The interaction graph is confirmed walkable without execution - [ ] Every interaction is confirmed keyboard reachable in an explicit order - [ ] The no-motion version is confirmed reviewed - [ ] An export is confirmed to run offline from a local file - [ ] The embed is confirmed to survive conflicting host styles ## Launch constraint Do not market omitted Genially capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Authored pieces. A document, stored as one. DOCUMENTS_DIR=~/Documents/interactive # Required. Self-contained bundles including the player. EXPORT_DIR=./exports # Required. Present as a constant. A hosted player is an expiry date on the content. BUNDLE_PLAYER=always # Required. Enforced at save for elements carrying meaning. REQUIRE_ALT_TEXT=always # Required. A long transition is an unresponsive interface. MAX_ANIMATION_MS=400 # Required. Off by default. Learners' interaction data is not collected without asking. INTERACTION_REPORTING=off
# Genially · indie build 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. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Model | Interactions as a declarative state machine over elements | an interaction described as data can be exported, tested and made accessible | | Runtime | A small player consuming the declaration | the player is what makes exported content work elsewhere | | Accessibility | Keyboard and screen reader paths derived from the same declaration | hotspots that only respond to a mouse exclude people entirely | | Export | Self-contained output including the player | content locked to a hosted viewer disappears when the account does | | Runtime stack | TypeScript with local documents | authored content is a document | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the editor and builds the player. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A screen reader you can actually use** · free - 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 - [ ] **A real interactive piece with a purpose** · free - 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 - [ ] **A deliberately hostile host page** · free - 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 - [ ] **Your accessibility standard, chosen up front** · free 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 ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - 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 If one of those is essential to you, that is the reason to keep paying for Genially, and the README should say so rather than pretend.
# Build brief · Genially The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me interactive content that still works when it leaves your platform to replace Genially. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Model: Interactions as a declarative state machine over elements · an interaction described as data can be exported, tested and made accessible - Runtime: A small player consuming the declaration · the player is what makes exported content work elsewhere - Accessibility: Keyboard and screen reader paths derived from the same declaration · hotspots that only respond to a mouse exclude people entirely - Export: Self-contained output including the player · content locked to a hosted viewer disappears when the account does - Runtime stack: TypeScript with local documents · authored content is a document ### Data model (create this before Phase 1) - 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. ### Phase 1 · Interactions as data, not as scripts Build: Model interactions as transitions between named states with declared effects. Support hotspots, layers and reveals through the same mechanism. Detect unreachable states and transitions that lead nowhere. Trap: 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. Done when: Interactions are stored as declarative transitions and the full interaction graph can be walked without executing anything. Unreachable states are detected and reported. A transition to a nonexistent state is impossible. The same mechanism handles hotspots, layers and reveals rather than three separate implementations. Do not build yet: No accessibility, player or export. ### Phase 2 · Usable without a mouse Build: Derive keyboard navigation from the interaction graph, with an explicit focus order. Announce state changes to assistive technology. Require alternative text for any element carrying meaning. Trap: 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. Done when: Every interactive element is reachable and triggerable by keyboard in an explicit order. A state change is announced to assistive technology rather than changing silently. An element carrying meaning cannot be saved without alternative text. The piece is navigable end to end with a screen reader, tested rather than assumed. Do not build yet: No player, export or animation yet. ### Phase 3 · Animation that can be turned off Build: Declarative animations on transitions, honouring the reduced-motion preference by replacing them with immediate changes. Verify the piece is comprehensible with all motion disabled. Trap: 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. Done when: Reduced motion replaces animations with immediate state changes throughout. The piece is reviewed with all motion disabled and the review is recorded. Content whose meaning depends on an animation is flagged. Animation duration is capped so a transition cannot block interaction for long. Do not build yet: No player or export yet. ### Phase 4 · A player small enough to ship Build: A player consuming the declaration, small and dependency free. Export a self-contained bundle including the player, the declaration and the assets. Verify the export runs from a local file with no network. Trap: 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. Done when: An exported bundle runs from a local file with networking disabled. The player is small enough to ship with every export, with its size measured and stated. Assets are embedded or bundled rather than referenced remotely. The exported piece keeps keyboard navigation and reduced-motion behaviour. Do not build yet: No embedding or analytics yet. ### Phase 5 · Embedding and knowing it worked Build: An embed that isolates from the host page's styles and does not block its loading. Report basic interaction completion where the author enables it. Handle a host page with conflicting styles and a restrictive content policy. Trap: 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. Done when: The embed renders correctly inside a page with deliberately conflicting styles. It loads asynchronously without blocking the host page. It functions under a restrictive content policy or fails with a clear message. Interaction completion reporting is off by default and states what it records when enabled. Do not build yet: Nothing after this. ### Out of scope (and why) - Template and asset libraries, which is most of what the hosted products actually sell. - Real-time collaborative authoring, which is a synchronisation project rather than an authoring one. - Learning management integration and grading, which is a standards-heavy field of its own. - Rich animation authoring comparable to a motion tool, which is a different discipline entirely. ### README must contain - That interactions are declarative transitions, which is what makes keyboard navigation, testing and export possible at all - That focus order is explicit rather than derived, because derived order follows the document rather than the meaning - That reduced motion is honoured and the no-motion version is reviewed rather than assumed to degrade gracefully - That the player is bundled with every export, so content does not expire with an account - That interaction reporting is off by default and states what it records
# Agent instructions · Genially indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Interactions as a declarative state machine over elements, A small player consuming the declaration, Keyboard and screen reader paths derived from the same declaration, Self-contained output including the player, TypeScript with local documents. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - 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. - 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. - 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. - 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.
# Build plan · Genially 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. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Interactions as data, not as scripts What a click does is described rather than programmed. ### Steps 1. Model transitions between named states A script per element rules out everything the later phases need. Files: `src/interactions.ts` 2. Walk the graph for reachability An unreachable layer is a mistake nobody finds by clicking. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Use one mechanism for all interaction kinds Three implementations diverge and only one gets accessibility. Files: `src/interactions.ts` ### Done when - [ ] The interaction graph walks without execution - [ ] Unreachable states are reported - [ ] Transitions to nonexistent states are impossible - [ ] One mechanism covers hotspots, layers and reveals ### Watch 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. ## Phase 2 · Usable without a mouse Every interaction can be reached and triggered by keyboard. ### Steps 1. Make focus order explicit Derived order follows the document, not the meaning. Files: `src/focus.ts` 2. Announce state changes A silent change is invisible to anyone not watching the screen. Files: `src/announce.ts` 3. Require alternative text for meaningful elements Enforced at save, not suggested in documentation. Files: `src/elements.ts` ### Done when - [ ] Every interaction is keyboard reachable in an explicit order - [ ] State changes are announced - [ ] Meaningful elements require alternative text - [ ] A screen reader traverses the piece end to end ### Watch 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. ## Phase 3 · Animation that can be turned off Motion is optional and the piece still communicates. ### Steps 1. Honour reduced motion everywhere It is set for medical reasons rather than as a preference. Files: `src/motion.ts` 2. Review the no-motion version Assuming graceful degradation is how information disappears. Files: `src/review.ts` 3. Cap animation duration A long transition is an unresponsive interface. Files: `src/motion.ts` ### Done when - [ ] Reduced motion replaces animation with immediate changes - [ ] The no-motion version is reviewed and recorded - [ ] Motion-dependent meaning is flagged - [ ] Animation duration is capped ### Watch 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. ## Phase 4 · A player small enough to ship The piece runs from a self-contained export. ### Steps 1. Bundle the player with the export A hosted player is an expiry date on the content. Files: `src/export.ts` 2. Keep the player small It ships with every export. Measure and state its size. Files: `player/` 3. Test the export offline From a file, with networking disabled. Files: `tests/export-offline.test.ts` ### Done when - [ ] An export runs from a local file offline - [ ] The player size is measured and stated - [ ] Assets are bundled rather than referenced - [ ] Accessibility survives the export ### Watch 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. ## Not in this build - Template and asset libraries. - Real-time collaborative authoring. - Learning management integration and grading. - Motion design authoring. ## After v1, if you want it - 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
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Authored pieces. A document, stored as one. DOCUMENTS_DIR=~/Documents/interactive # Required. Self-contained bundles including the player. EXPORT_DIR=./exports # Required. Present as a constant. A hosted player is an expiry date on the content. BUNDLE_PLAYER=always # Required. Enforced at save for elements carrying meaning. REQUIRE_ALT_TEXT=always # Required. A long transition is an unresponsive interface. MAX_ANIMATION_MS=400 # Required. Off by default. Learners' interaction data is not collected without asking. INTERACTION_REPORTING=off
# Genially · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Genially, build interactive presentations with hotspots, layers, and simple animations. The hard boundary is template marketplace, hosted embeds, analytics, collaboration, and education workflows, plus editor polish, assets, collaboration, and distribution. ## Product outcome Interactive content that can be navigated with a keyboard, understood with motion disabled, and still works years later from an exported file with no platform behind it. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - modern desktop browser - local fonts and images - optional image-generation API key ## Explicit non-goals for v1 - 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 ## Success criteria - The interaction graph is confirmed walkable without execution - Every interaction is confirmed keyboard reachable in an explicit order - The no-motion version is confirmed reviewed - An export is confirmed to run offline from a local file - The embed is confirmed to survive conflicting host styles
# Build brief · Genially The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me interactive content that still works when it leaves your platform to replace Genially. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Model: Interactions as a declarative state machine over elements · an interaction described as data can be exported, tested and made accessible - Runtime: A small player consuming the declaration · the player is what makes exported content work elsewhere - Accessibility: Keyboard and screen reader paths derived from the same declaration · hotspots that only respond to a mouse exclude people entirely - Export: Self-contained output including the player · content locked to a hosted viewer disappears when the account does - Runtime stack: TypeScript with local documents · authored content is a document ### Data model (create this before Phase 1) - 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. ### Phase 1 · Interactions as data, not as scripts Build: Model interactions as transitions between named states with declared effects. Support hotspots, layers and reveals through the same mechanism. Detect unreachable states and transitions that lead nowhere. Trap: 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. Done when: Interactions are stored as declarative transitions and the full interaction graph can be walked without executing anything. Unreachable states are detected and reported. A transition to a nonexistent state is impossible. The same mechanism handles hotspots, layers and reveals rather than three separate implementations. Do not build yet: No accessibility, player or export. ### Phase 2 · Usable without a mouse Build: Derive keyboard navigation from the interaction graph, with an explicit focus order. Announce state changes to assistive technology. Require alternative text for any element carrying meaning. Trap: 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. Done when: Every interactive element is reachable and triggerable by keyboard in an explicit order. A state change is announced to assistive technology rather than changing silently. An element carrying meaning cannot be saved without alternative text. The piece is navigable end to end with a screen reader, tested rather than assumed. Do not build yet: No player, export or animation yet. ### Phase 3 · Animation that can be turned off Build: Declarative animations on transitions, honouring the reduced-motion preference by replacing them with immediate changes. Verify the piece is comprehensible with all motion disabled. Trap: 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. Done when: Reduced motion replaces animations with immediate state changes throughout. The piece is reviewed with all motion disabled and the review is recorded. Content whose meaning depends on an animation is flagged. Animation duration is capped so a transition cannot block interaction for long. Do not build yet: No player or export yet. ### Phase 4 · A player small enough to ship Build: A player consuming the declaration, small and dependency free. Export a self-contained bundle including the player, the declaration and the assets. Verify the export runs from a local file with no network. Trap: 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. Done when: An exported bundle runs from a local file with networking disabled. The player is small enough to ship with every export, with its size measured and stated. Assets are embedded or bundled rather than referenced remotely. The exported piece keeps keyboard navigation and reduced-motion behaviour. Do not build yet: No embedding or analytics yet. ### Phase 5 · Embedding and knowing it worked Build: An embed that isolates from the host page's styles and does not block its loading. Report basic interaction completion where the author enables it. Handle a host page with conflicting styles and a restrictive content policy. Trap: 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. Done when: The embed renders correctly inside a page with deliberately conflicting styles. It loads asynchronously without blocking the host page. It functions under a restrictive content policy or fails with a clear message. Interaction completion reporting is off by default and states what it records when enabled. Do not build yet: Nothing after this. ### Out of scope (and why) - Template and asset libraries, which is most of what the hosted products actually sell. - Real-time collaborative authoring, which is a synchronisation project rather than an authoring one. - Learning management integration and grading, which is a standards-heavy field of its own. - Rich animation authoring comparable to a motion tool, which is a different discipline entirely. ### README must contain - That interactions are declarative transitions, which is what makes keyboard navigation, testing and export possible at all - That focus order is explicit rather than derived, because derived order follows the document rather than the meaning - That reduced motion is honoured and the no-motion version is reviewed rather than assumed to degrade gracefully - That the player is bundled with every export, so content does not expire with an account - That interaction reporting is off by default and states what it records
# Architecture · Genially ## Stack | Part | Choice | Why | | --- | --- | --- | | Model | Interactions as a declarative state machine over elements | an interaction described as data can be exported, tested and made accessible | | Runtime | A small player consuming the declaration | the player is what makes exported content work elsewhere | | Accessibility | Keyboard and screen reader paths derived from the same declaration | hotspots that only respond to a mouse exclude people entirely | | Export | Self-contained output including the player | content locked to a hosted viewer disappears when the account does | | Runtime stack | TypeScript with local documents | authored content is a document | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Interactions | Declarative state transitions. | Not swappable for per-element scripts, which rule out every later phase. | | Accessibility | Explicit focus order and announcements. | Any implementation, provided focus order is authored rather than derived. | | Motion | Optional animation with a reviewed alternative. | Any implementation, provided the no-motion version is checked. | | Player | A small bundled runtime. | Any player, provided it ships with the export. | | Embed | Style isolation and asynchronous loading. | Any technique, provided it survives a hostile host page. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DOCUMENTS_DIR` · required · Authored pieces. A document, stored as one. - `EXPORT_DIR` · required · Self-contained bundles including the player. - `BUNDLE_PLAYER` · required · Present as a constant. A hosted player is an expiry date on the content. - `REQUIRE_ALT_TEXT` · required · Enforced at save for elements carrying meaning. - `MAX_ANIMATION_MS` · required · A long transition is an unresponsive interface. - `INTERACTION_REPORTING` · required · Off by default. Learners' interaction data is not collected without asking. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Genially product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Interactions as a declarative state machine over elements, A small player consuming the declaration, Keyboard and screen reader paths derived from the same declaration, Self-contained output including the player, TypeScript with local documents. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - 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. - 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. - 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. - 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. - 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.
# Delivery milestones · Genially Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Interactions as data, not as scripts What a click does is described rather than programmed. ### Steps 1. Model transitions between named states A script per element rules out everything the later phases need. Files: `src/interactions.ts` 2. Walk the graph for reachability An unreachable layer is a mistake nobody finds by clicking. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Use one mechanism for all interaction kinds Three implementations diverge and only one gets accessibility. Files: `src/interactions.ts` ### Done when - [ ] The interaction graph walks without execution - [ ] Unreachable states are reported - [ ] Transitions to nonexistent states are impossible - [ ] One mechanism covers hotspots, layers and reveals ### Watch 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. ## M2 · Usable without a mouse Every interaction can be reached and triggered by keyboard. ### Steps 1. Make focus order explicit Derived order follows the document, not the meaning. Files: `src/focus.ts` 2. Announce state changes A silent change is invisible to anyone not watching the screen. Files: `src/announce.ts` 3. Require alternative text for meaningful elements Enforced at save, not suggested in documentation. Files: `src/elements.ts` ### Done when - [ ] Every interaction is keyboard reachable in an explicit order - [ ] State changes are announced - [ ] Meaningful elements require alternative text - [ ] A screen reader traverses the piece end to end ### Watch 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. ## M3 · Animation that can be turned off Motion is optional and the piece still communicates. ### Steps 1. Honour reduced motion everywhere It is set for medical reasons rather than as a preference. Files: `src/motion.ts` 2. Review the no-motion version Assuming graceful degradation is how information disappears. Files: `src/review.ts` 3. Cap animation duration A long transition is an unresponsive interface. Files: `src/motion.ts` ### Done when - [ ] Reduced motion replaces animation with immediate changes - [ ] The no-motion version is reviewed and recorded - [ ] Motion-dependent meaning is flagged - [ ] Animation duration is capped ### Watch 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. ## M4 · A player small enough to ship The piece runs from a self-contained export. ### Steps 1. Bundle the player with the export A hosted player is an expiry date on the content. Files: `src/export.ts` 2. Keep the player small It ships with every export. Measure and state its size. Files: `player/` 3. Test the export offline From a file, with networking disabled. Files: `tests/export-offline.test.ts` ### Done when - [ ] An export runs from a local file offline - [ ] The player size is measured and stated - [ ] Assets are bundled rather than referenced - [ ] Accessibility survives the export ### Watch 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. ## M5 · Embedding and knowing it worked (production only) The piece works inside someone else's page and you can tell whether it did. ### Steps 1. Isolate the embed's styling Inheriting the host stylesheet breaks it differently on every site. Files: `player/embed.ts` 2. Load asynchronously It is on someone else's page and must not slow it down. Files: `player/embed.ts` 3. Make reporting opt in and explicit Learners' interaction data is not collected by default. Files: `src/analytics.ts` ### Done when - [ ] The embed survives conflicting host styles - [ ] It loads without blocking the host page - [ ] A restrictive policy fails clearly - [ ] Reporting is off by default and states what it records ### Watch 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.
# Operations · Genially ## Backup Authored documents are the work and exports are derivable from them. Back up the documents and the assets, and keep at least one export as a check that the pipeline still reproduces what you published. ## Restore Restore and confirm exports still reproduce identically, since a difference means the player version changed and previously published content may behave differently from a fresh export of the same document. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For an authoring tool the checks live in the editor: unreachable states, missing alternative text and motion-dependent meaning are all caught before publication, which is the only useful moment. ## Incident checklist Published content that stops working is the failure this build exists to prevent, and it happens when something was referenced rather than bundled. The offline export test is the control. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] The interaction graph is confirmed walkable without execution - [ ] Every interaction is confirmed keyboard reachable in an explicit order - [ ] The no-motion version is confirmed reviewed - [ ] An export is confirmed to run offline from a local file - [ ] The embed is confirmed to survive conflicting host styles ## Launch constraint Do not market omitted Genially capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Authored pieces. A document, stored as one. DOCUMENTS_DIR=~/Documents/interactive # Required. Self-contained bundles including the player. EXPORT_DIR=./exports # Required. Present as a constant. A hosted player is an expiry date on the content. BUNDLE_PLAYER=always # Required. Enforced at save for elements carrying meaning. REQUIRE_ALT_TEXT=always # Required. A long transition is an unresponsive interface. MAX_ANIMATION_MS=400 # Required. Off by default. Learners' interaction data is not collected without asking. INTERACTION_REPORTING=off
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for Genially because people pay for a vast ready-made design system and a collaboration surface that behaves predictably under deadline. The recurring cost buys canvas correctness, fonts, exports, templates, asset rights, multiplayer state, and accessibility, not just the visible interface.
xtemplate marketplace, hosted embeds, analytics, collaboration, and education workflows
xhuge template and asset libraries
xreal-time multiplayer
xbrand governance
xprofessional print workflows
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Genially pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free | $0/user | $0/user | 2 GB storage and 500 AI credits; PPTX, Google Slides and PDF import. |
| pro | custom | — | Paid individual plan; exact numeric storage/AI caps and USD price were not exposed in the public rendered page. |
| master | custom | — | Advanced creation/collaboration plan; exact numeric caps and USD price were not exposed. |
| author | custom | — | Includes SCORM 1.2/2004 publishing; exact numeric caps and USD price were not exposed. |
| enterprise | custom | — | Custom seats, security, SCORM and support; public price is not listed. |
free tier2 GB storage and 500 AI credits.
billingPro is available monthly; Master and Author are annual-only; Enterprise is annual/custom
hidden costsHigher plans are annual commitments and team pricing is seat-dependent. Bank/SEPA payment is only offered for purchases above $600; NGO discounts can reach 50%.
verified 2026-08-14 · source ↗
Vibecode Genially
Kinda. The core of Genially is buildable in a weekend with the prompt on this page, but there are real gaps: template marketplace, hosted embeds, analytics, collaboration, and education workflows, huge template and asset libraries. Read the honest list above before committing.
How much does Genially cost?
Genially's pricing is usage-based or varies by plan · Public pricing is localized and can require account context; canonical price is null..
What do I lose by replacing Genially?
Honestly: template marketplace, hosted embeds, analytics, collaboration, and education workflows; huge template and asset libraries; real-time multiplayer; brand governance; professional print workflows. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Genially?
Yes: Lumi H5P Desktop Editor (An installable H5P authoring app for hotspots, branching slides, interactive video, and simple games; distinctly education-shaped, but real.) The prompt is for when you want it exactly your way.