Ground & Form F4xAPIStudio Pilot

Pilot build · Windows and Linux

Build the course.
Every block can feed back or forward.

F4xAPI Studio is a desktop course builder that hands you the reporting. You choose what counts as done, what an attempt is worth, and what the course should do about it while the learner is still inside it. Send someone back through the practice they need. Leave the reading they understood alone.

Build the course.
Decide what it reports.

F4xAPI Studio is a desktop course builder for people who write code. Every block emits xAPI, and every statement passes through your override before it leaves the page. Sandboxed HTML blocks, Monaco exercises, a theme layer of custom properties and classes, and an open package format.

Authoring runs offline · Windows and Linux · Build 11 September 2026

Authoring
EditPreview Verify · Export
Chapters
◈
</>
⇄
{;}
Canvas
Properties
xAPI behaviour
override · fb-code · escalation-quiz
// Three failures — send them back through the exercises only. state.fails = (state.fails || 0) + 1; if (!statement.pass && state.fails >= 3) { course.resetChapter('patterns', { type: 'fb-code' }); course.goTo('patterns'); state.fails = 0; } emit('answered', { result: { score: statement.score, completion: statement.pass, } });
Preview
Patterns
Try it
Continue

Completion tells you someone reached the end. It never tells you what the end cost them.

Every argument on this page comes back to that sentence. Peirce would call that number a sign: it constructs meaning rather than reporting it, and a course that cannot answer past it is a document with a progress bar.

The problem this exists for

You finished the course.
Is it any good?

Twelve chapters. A simulation you are proud of. A final assessment with a pass mark someone agreed to in a meeting. It goes out, and six weeks later the LMS says 84% completed. That number is the whole report.

Completion says a learner reached the end. It says nothing about the route. Nobody can tell you that a third of them opened the glossary four times on chapter six, that the exercise everyone passes takes eleven attempts to pass, or that the elegant interaction on page two is being skipped by anyone who finds the Continue button first.

xAPI already carries that detail. Every interaction can become a statement: who did what, to which thing, with what result. The catch is that most authoring tools decide the statements for you, and their idea of "done" is usually "seen".

F4xAPI hands the statements back. You choose which interactions count, what an attempt is worth, and what a result means. You can read those signals while the learner is still in the course and act on them: reset a section someone clearly did not absorb, score a simulation on the decisions made rather than the outcome reached, send a learner back through the practice while leaving the reading they understood intact.

Instrument a course this way and it starts answering questions about itself. Where do people stall. What gets retried. Which explanation costs three attempts and which costs none. Dewey's argument was that learning happens in the exchange between a person and a situation rather than in delivery; a course that cannot see the exchange has no way to improve it. That is a design instrument as much as a course builder, and it is why this tool exists.

Completion tells you someone reached the end. It never tells you what the end cost them.

Every feature further down this page exists to answer the second half of that sentence.

Statements, live
chapter 6 · one learner · one run
What it exposes
Attempts before pass11
Answers changed7
Reopened glossary4×
Sent back by an overrideonce

Why this, why now

A model will write you a flight simulator this afternoon.

Ask for a management simulation or a branching negotiation and you will have something that runs before the meeting to scope it has finished. The artefact stopped being the hard part. Whether it teaches anything is still yours — and the flight simulator was Forrester's and Sterman's idea for exactly that reason: compress the delay between a policy and its consequence until someone feels it instead of reading about it.

What generation made cheap

  • A working interactive artefact, in an afternoon
  • A plausible interface for almost any scenario
  • Scoring logic, if you can describe the rubric
  • Ten variants when the client changes their mind
  • Code you can read but would not have written from scratch

What it left on your desk

  • Whether it teaches. A simulation that runs is not a simulation that works
  • Wiring it into the course. Gates, refs, resets, what counts as done
  • The shape of the whole thing. Where the loops are, and whether they close
  • The evidence. Which signals you keep, and what they let you conclude
  • The judgement call when the model's rubric and your pedagogy disagree

The two jobs are converging, and this is where they meet.

A designer who can hold a system in their head is worth more as the bar drops, not less: generation raises what a client will ask for, and someone has to keep the result coherent. A developer dropped into course work reinvents gates and remediation badly, because they do not look like routing and state.

Both get the same primitives. Refs, gates, resets, reveals and signals are first-class, so the designer wires logic without a build step and the developer gets the pedagogy already named. The handover becomes a file, not a document.

The same file, two readings
Designer Decides what counts as done, and where a failure sends someone
Developer Writes the interaction the scenario needs and reports it properly
Both Read the same flow, the same matrix, the same live statements
Neither Maintains a repo, a bundler or a deployment to make it run

One course, three honest descriptions

Everyone touching the elephant is telling the truth.

The one holding the trunk says snake. The one at the leg says tree. The one at the ear says fan. Nobody is lying and nobody is right, because each of them has hold of a real part and none of them has hold of the animal.

The trunk

"It moves, one thing after another."

The course as a learner meets it: a sequence, start to finish.

The leg

"It is solid, and it holds things up."

The course as a structure: chapters, sections, blocks, properties.

The ear

"It is wide, and it doubles back."

The course as a system: gates, loops, coupling between chapters.

Most tools hand you the structure, some add a preview, almost none show the system — which is where the interesting failures live. Three views of one file, switchable while you work.

EditChapter 2 · Materials · same file
EditPreviewFlow Verify · Export
Chapters
◈
▶
{;}
⇄
Canvas
Properties

Chapter 2 · Materials

Choosing a material

PETG needs a hotter nozzle than PLA. Below 230°C it will not bond, and the rig refuses to start rather than laying down a part that comes apart in the hand.

Try it
▮ locked Continue opens when both exercises report a pass
Chapter 2 · Materials Watch video · callout × Practice 2 exercises × Check the rig resets section · feedback the block selected in Edit lives here

Swipe the diagram →

What this view tells you

What it cannot

Schön split this two ways: reflection-in-action, which happens while you are inside the work, and reflection-on-action, which happens when you step back. An override is the first. The flow and the matrix are the second. The mistake is not picking the wrong view — it is owning only one of them and calling the result a course.

Systems thinking, applied to a course

A course is a process. Read it like one.

Engineering has reasoned about coupled systems for decades. Learning design has a storyboard. Two of those instruments are in the tool: a BPMN flow of how the course behaves, and a matrix of what depends on what.

OrchestrationChapter 2 · Materials
Chapter 2 · Materials + Watch video · callout experienced · answered ↪ 1 × + Practice 2 exercises answered ƒ override ↩ 1 × + Check the rig · custom HTML progressed · completed ƒ override ↩ 2 resets section · feedback reveals · feed-forward

Swipe the diagram →

Read the triangles. Marks below the diagonal are remediation, marks above it are escalation, and a number is how many signals share the cell. The two pulsing far from the diagonal are the ones worth arguing about: distance from the diagonal is distance travelled, and both of these move a learner across the whole course in one statement. Ackoff's point about local optimisation is the whole reason to draw this: tidying one chapter in isolation is how the next one breaks. Fold a chapter by its arrow and its signals roll up rather than disappear.

Notation you already read

BPMN, not a bespoke diagram

Sections are sub-processes, gates are gateways, a thin circle opens a chapter and a heavy one closes it. Dashes animate toward the arrowhead, so direction reads before the label does.

Deterministic layout

The same course always draws the same

Nothing drags, nothing is measured from the DOM. Edges run in reserved channels above and below the row, so an arrow never crosses a task and a crossing hops rather than reading as a join.

Conditional gating

Five rules, and a runtime override

View, interaction, all items, score threshold, or your own predicate. Then show(), hide() and setRequired() let a running course change what it asks of the learner in front of it.

Feedback and feed-forward

Both directions, both drawn

Backwards is remediation: reset a block, a section or a chapter. Forwards is escalation: reveal the harder path for someone clearly ahead. A course doing both is a control system.

Process design checks

A contradiction will not save

Two goTo calls on one branch, or a hide of the thing you then move to: valid JavaScript, so a language service has nothing to say. The conflict checker marks them as errors and disables Save until they are gone, so the override never reaches the project. Two goTo calls in opposite arms of an if are correct and are left alone.

Drill down, not out

Chapter, section, block, container

The overview rolls feedback up to sections; open one to find the block driving it. Tabs, accordions and card decks open too.

The thing it is actually for

Complicated courses, without a toolchain.

Ambitious courses do not die on the idea. They die on the repo, the bundler, the dev server and the two years of maintaining all three. The editor is inside the tool, so the ambitious version costs about what the ordinary one does.

Monaco with autocomplete over the block's own props and the course API. Preview is the running course, not a refresh cycle. The output is a package, not a project someone inherits.

No build step, no node_modules, and no second machine to reproduce before a colleague can open your work.

Edit

Monaco, in the properties panel

Overrides, custom HTML and code exercises all edited where the block lives, with completion for the whole scope and five editor themes.

Run

Preview with the gates on

Desktop, tablet and phone frames, learner progress kept across edits, and a reviewer bypass that no export can contain.

Ship

One file out

H5P or SCORM, verified first, with no repository, pipeline or hosting arrangement left behind for someone to look after.

For learning designers

A loop, watched from the outside

Someone is failing at the printer. Watch what the course does about it.

The capstone of the built-in course is a 3D printer that runs and reports nothing. The learner has to wire up its reporting. Here is a run that goes wrong, and the rule that turns it into a lesson instead of a wall.

FormaJet rig · learner view
FormaJet · bed 60°C
Material
PLA PETG ABS
Nozzle
190°C PETG needs 230
0230 minimum300
Idle. Choose a material, set the nozzle, start the print.
Start print
What the course is told
Where the learner stands
Materials · the readingDone
Materials · exercise 1Done
Materials · exercise 2Done
Capstone · the rigHere
01 / 06

The commission. The machine works. Nothing it does reaches the course, so nothing the learner does is worth anything yet.

SCORM 1.2 export
What the LMS can store: one score, one status, pass or fail.
H5P export · your own xAPI
What the same run looks like when nothing has to be collapsed.
Both columns are the same learner, the same four attempts. SCORM has a fixed vocabulary, so the bridge rolls everything into the values it defines. The left column is completion. The right column is what the end cost them — which is why the left one cannot tell you that the rewind happened, or that the first three failures were all the same mistake.

The rule that sent them back is six lines.

It sits in the properties panel beside the block, not buried in the content. Three failures, and the learner is returned to the exercises in the materials chapter. Only the exercises. The reading they already understood stays finished, because making someone re-read what they knew reads as punishment and they stop trusting the course.

Change 3 to 2 and the loop tightens. Change 'materials' and it points somewhere else. That is the whole edit.

printer-rig · xAPI behaviour
state.fails = (state.fails || 0) + 1; if (!statement.pass && state.fails >= 3) { course.resetChapter('materials', { type: 'exercise' }); course.goTo('materials'); state.fails = 0; }

Autocomplete covers the whole scope, the built-in course walks you through this exact pattern, and a mistake here reports nothing rather than breaking the block.

Design decisions, not settings

Things every course quietly decides for you. Here you decide them.

Most of them are continuums that tooling flattens into booleans. Done, correct, ready: each one has degrees, and the degrees are where the information was.

What counts as done

Seen, or understood?

A block can complete on sight, on any interaction, on every item being opened, on a score you set, or on a rule you write. The accordion that completes when one row opens in an onboarding course can demand all five in a compliance one, with no change to the accordion.

What an attempt is worth

Full credit, or fading credit

Keep the best attempt, keep only the first, pay less for each retry, give half marks for an answer that is one step off. Retries still report, so a learner who kept going does not look like one who walked away.

Where someone goes next

Back a page, or back a chapter

Reopen one block, one subsection, or a whole chapter — and filter by the kind of block, so a rewind can hit the exercises and leave the reading alone. Each reset reports how much it actually undid.

What they are told about it

A reason, not a bounce

A learner moved without explanation assumes the course is broken. The preview shows the banner they will see, and the wording is yours: say what to review, not that they failed.

What you find out

Attempts, changes, stalls

Answers changed before submitting. Rows reopened. Attempts before a pass. Blocks where progress stops and never resumes. The matching block reports how much reworking a learner needed, which is a different question from whether they got it right.

What survives the LMS

Everything, or one number

Export to H5P and every statement arrives with its verb and result. Export to SCORM and the bridge collapses it to one score and one status, because that is all SCORM defines. Knowing which you are shipping into changes what is worth measuring.

Look and feel

One theme, and everything follows it.

The theme designer sets brand colours, type, corners, spacing and a course logo. Every themed piece carries a class, so chapter titles, callouts, cards, tab headers, buttons, progress bars and feedback states move together — including the code editor a learner types into, which takes its own theme so a dark course does not end up with a white editor in the middle of it.

The five below are illustrations rather than presets, built to show how far the theme layer moves. Press one and watch both panes change together.

Learner viewGround & Form
Chapter 3 · Materials

Choosing a material

PETG needs a hotter nozzle than PLA. Below 230°C it will not bond, and the rig refuses to start rather than laying down a part that comes apart in the hand.

TipSet the nozzle before the bed. The bed follows the material; the nozzle decides whether it prints at all.
Continue✓ Answer correct
printer-rig.js forge-light
1
2
3
4
5
6
7
8
9
// three refusals, then send them back
state.fails = (state.fails || 0) + 1;

if (!statement.pass && state.fails >= 3) {
  course.resetChapter('materials', { type: 'exercise' });
  course.goTo('materials');
  state.fails = 0;
}
JavaScriptLn 4, Col 1 Monaco · forge-light

These five are illustrations, not shipped presets — you build your own in the theme designer, from colours, type, corners and spacing. The editor themes are real: forge-light is the default, with paper, slate, midnight and contrast alongside it.

Custom interactions

If your assistant can write it, the course can run it.

A Custom HTML block takes markup, styles and script and runs them in a sealed frame. This is where a model-generated interaction goes: paste it in, give it the four calls it needs to report back, and it becomes a gradeable part of the course rather than a decoration.

The printer in the built-in course is one of these. It reports material selection, temperature changes, a refused start and a finished print as separate statements, which is why a rule could tell the difference between someone who is stuck and someone who is experimenting.

  • To reportforge.complete() · forge.score(raw, max) · forge.emit(verb, result) · forge.resize(). That is the entire surface.
  • ContainmentThe frame cannot read your page, touch storage or reach the network, so a pasted fragment cannot do damage you did not intend.
  • PreviewIt runs for real in Preview under the same constraints it will meet in the LMS, on a desktop, tablet or phone frame.
Custom HTML · the four calls
// when the print finishes forge.emit('completed', { score: { raw: quality, max: 100 }, completion: true }); // when the machine refuses to start forge.emit('progressed', { completion: false });
Inherited from the theme
--brand-primary --page-bg --brand-accent --surface --text-primary --surface-border --font-title --radius --font-body --content-width

For front-end developers

The surface area

Control of the signal, and of the experience that produces it.

01 · Override framework

A few lines of JavaScript decide what a block means.

Every component ships a default statement. A callout reports experienced when it is seen, a question reports answered with a score. An override replaces that default with your own code, written in Monaco with autocomplete for the whole scope, and attached to the block rather than forked into it. The same accordion can complete on one interaction in one course and only after every row is read in another.

  • Scopeemit, state, statement, props. State persists for the life of the block, so attempts and partial progress are countable.
  • Verbsexperienced, interacted, answered, completed, progressed. A typo is dropped at the bridge rather than reaching the gradebook as a novel activity type.
  • Reachcourse.get(), uncomplete(), reset(), goTo(), resetSection(), resetChapter() — addressed by ref, so inserting a chapter never breaks a pointer.
  • FaultsA throwing override reports nothing instead of taking the block down, and statements emitted before the fault are kept.
  • ConflictsContradictory calls — two goTo calls on one branch, a hide followed by a move to the same ref — are marked as errors in the gutter and Save stays disabled until they are fixed, so they never reach a project. Ambiguous cases, like two goTo calls in opposite arms of an if, are left alone.
Properties · xAPI behaviour
fb-accordion · glossary
// Require every row, not just one. state.viewed = state.viewed || []; if (!state.viewed.includes(statement.itemIndex)) state.viewed.push(statement.itemIndex); const total = props.items.length; const seen = state.viewed.length; emit('progressed', { result: { score: { raw: seen, max: total, scaled: seen / total }, completion: seen === total, } });
Progression
view_required80% visible for 2s
interactionany completion result
score_thresholdscaled ≥ your mark
all_itemsone per sub-item
customyour predicate

02 · Custom HTML

Write the interface the training actually needs.

A Custom HTML block runs your fragment in its own frame with a unique opaque origin. It cannot read the page around it, touch storage or make network calls, and it reports back through one object: forge.complete(), forge.score(), forge.emit(), forge.resize(). A machine simulation can report material choice, a refused start and a finished run as three separate statements, which is detail no host could have inferred from outside.

The frame receives the theme as custom properties, and every themed element carries a class you can style globally.

Theme classes
.thm-chapter-title .thm-callout .thm-body .thm-card .thm-button .thm-tab .thm-continue .thm-question .thm-progress .thm-toc-item .thm-answer-correct .thm-feedback-pass .thm-code-frame .thm-feedback-fail
--brand-primary --page-bg --radius --brand-accent --surface --font-title --text-primary --surface-border --font-body

The theme layer

Tokens out, classes over the top, editor included.

A theme is a token set plus element and component overrides, adopted through a constructable stylesheet so a component that mounts mid-session is correct on its first paint rather than after the next panel touch. Code surfaces carry their own palette — the exercise editor, the read-only samples and the code frame follow the theme's editor tokens instead of a hard-coded white. Editor themes are forge-light by default, with paper, slate, midnight and contrast.

Values are sanitised on the way in: url(), expression() and @import are rejected, and structural properties — position, display, z-index, overflow, width — are absent from the allowlist entirely, so a theme cannot hide navigation or collapse the page.

03 · Code exercises

Ask for code, and mark it.

A code exercise is a Monaco editor with a Validate action. Match against up to twenty accepted answers, add requirements that must appear regardless of shape, or post the submission to your own test service and score whatever comes back. Submitted code is never executed in the course.

  • LanguagesJavaScript, TypeScript, Java, C#, C++, C, Python, SysML, SQL, HTML, CSS, JSON, shell, plain text.
  • MarkingAnswer matching that ignores formatting, extra requirements with their own failure messages, or a validation service over HTTPS.
  • AttemptsCap them, reveal the answer when they run out, or write an override that pays less credit for each retry and reports the shape of the struggle.
  • CompanionA read-only Code Sample block with line numbers and emphasis, for showing a technique before you ask for it.
Code exercise
javascript · attempt 2 of 5
1 // Report a score built from state and props. 2 const seen = state.viewed.length; 3 emit('progressed', { result: { 4 score: { raw: seen, max: props.items.length }, 5 } });
Validate requirement: must use props
Result
✓ matches accepted answer 3
✓ uses props
✕ no max supplied on the score
scaled 0.66 · credit 0.8 on attempt 2

For front-end developers

Documentation where you are typing

Every prop a block takes, in the editor with you.

Each component declares a schema. That schema builds the properties panel and feeds autocomplete inside the override editor, so props. lists what this block actually has, with types, rather than what you remember from the last one.

xAPI behaviour fb-matching
// scope: emit · state · statement · props · course
const total = props.
JavaScript Monaco · forge-light
Course API
course.get(ref) // { complete, scaled, attempts, title } course.uncomplete(ref) // close the gate, keep the score course.reset(ref) // clear it entirely course.goTo(ref) // move the learner course.refs() // every ref in the course course.resetTo(ref) // that block and everything after course.resetSection(ref) // one subsection course.resetChapter(ref, { type }) course.show(ref, msg) // reveal remedial or advanced content course.hide(ref, msg) // neither shown nor counted at a gate course.isShown(ref) course.required(ref) course.setRequired(ref, value, msg)
  • In appFour topics from the toolbar: getting started, writing an xAPI override, custom HTML blocks, code exercises. Each with runnable examples.
  • Tested docsThe worked examples are executed by the test suite — the abacus arithmetic, the tic-tac-toe win detection, the per-turn reporting — and every code block in the reference is syntax-checked. An example that has drifted is worse than none.
  • Typed modelBlock, ComponentKey, ProgressionConfig, AnimationConfig, XAPIStatement and the canvas message union are all declared, so an editor can tell you what a field accepts before you save.
  • Service contractCode exercises can post a submission to your own endpoint over HTTPS and score the response. The request and response shapes are documented and stable.

Where the code runs

A desktop tool, and three sandboxes it never lets you skip.

Authoring is local. Untrusted markup is contained by construction rather than by a setting someone can forget to switch on.

Local by default

Your projects stay on your machine

Projects, assets, drafts and snapshots are files on your disk. Authoring works with the network off — on a plane, on an air-gapped review laptop, in a room where the content cannot leave. The network is needed for the licence check and for compilation, and what crosses it is project data you chose to export, not a mounted workspace.

Three surfaces, three treatments

The canvas never runs your markup

The design canvas shows Custom HTML as inert source — no iframe, no Blob URL, no postMessage. Preview runs it for real in the sandbox. The exported package emits that same sandbox. Tests assert containment on both live surfaces and assert the canvas frames nothing.

The sandbox contract

Opaque origin, no way home

Each block loads in its own frame with a unique opaque origin. allow-same-origin is never granted beside allow-scripts, the CSP withholds connect-src so a block cannot phone anywhere, and message source windows are checked before a statement is believed. A pasted fragment cannot read your page, your storage or your cookies.

Learner code

Submissions are never executed

A code exercise matches a submission against accepted answers, checks it against your requirements, or posts it to a validation service you control. Nothing runs learner code in the course, so a clever answer is a marking problem rather than an execution risk.

Sanitisers under attack

Two of them, tested adversarially

The inline HTML sanitiser takes 24 XSS payloads in the suite — scheme obfuscation, control-character smuggling, style-based exfiltration, handler casing — and legitimate formatting has to survive them. Theme values get their own: url(), expression() and @import are rejected, and structural properties are not in the allowlist at all.

Overrides contain mistakes

Not a privilege boundary, and not sold as one

An author editing their own project already controls it, so there is no escalation to prevent. What the runtime contains is error: a fixed four-name scope, a verb allowlist enforced at the bridge so a typo cannot reach the LMS as a novel activity type, and a throwing override that reports nothing rather than taking the block down.

Files that fail closed

Opaque at a glance, honest about it

A .forge5p is canonical JSON, deflated and base64url-encoded inside a delimited text envelope, and checksummed — so corruption and truncation surface on open instead of half-loading. That is obfuscation, not encryption, and the API says so in as many words: two lines will decode and inflate it. When confidentiality is the actual requirement, serializeEncrypted() gives AES-256-GCM with a PBKDF2-derived key, a fresh salt and nonce per export, and a wrong passphrase that fails to decrypt rather than yielding altered content.

Licensing and compilation

Signed on the server, verified offline

Client code cannot be made secret, so the client is treated as public and the machinery assumes it. Activation exchanges a key for an RS256 token carrying tier, seats, expiry and a machine fingerprint; the client verifies the signature offline against a public key compiled into the binary, so the private key never touches a machine you hand out. Authoring keeps working without a network for a thirty-day grace window, and the fingerprint is compared by similarity rather than equality, so new RAM does not cost somebody their seat.

Compilation sits behind that check, which is what stops an unlicensed copy shipping a package. It takes arbitrary author-supplied HTML and never evaluates it, payloads are size-capped and rate-limited per licence, and a compile takes a per-seat lock so one seat cannot fan out into a build farm.

Built in · not a PDF

The tool ships with the course that teaches it.

Ten chapters, authored in F4xAPI Studio and opened from the project list. It is the thing it teaches: it gates on the work you do, marks what you write, sends you back when you have not got it, and ends with the printer from the story above. Learn the tool by being a learner in it.

01
The pieces
Chapters, subsections, blocks, and what a gate is.
02
How reporting works
Verbs, results, and why the verb alone changes nothing.
03
Wiring your first gate
Progression types against real content.
04
When a gate misfires
Four broken courses, four faults to find.
05
Choosing between designs
No correct answer, only a defensible one.
06
One statement at a time
Reading an override before writing one.
07
Patterns worth knowing
Count items, keep the best attempt, decay by attempt, progress without completion.
08
Repair, then finish
Fixing overrides that report the wrong thing.
09
Sending a learner back
Remediation by ref, without punishing the reading.
10
Capstone — wire up the printer
A running machine with no reporting. Make it report.

Logic you would otherwise write yourself

The awkward behaviour is already handled.

Every one of these is a day of work and a fortnight of edge cases when you build it by hand for one course.

Multi-video autoplay

One at a time, in the right order

Tick autoplay on three videos and most tools start all three. Here they queue: only what is on screen, in document order, each waiting for the last. Finished means what the block says — a video completing on questions releases the queue when they are answered. One the learner abandoned is never restarted for them.

Interactive video

Questions on the timeline

Questions on the timeline, each reporting answered with its score. Done means watched, answered, or either — your choice. Captions and poster where you need them.

Media centre

One library, every project

Stored once locally, picked into the projects that use it, with folders and search. Imports run on a queue that outlives the panel. Images carry their bytes into the package; video is referenced by stream URL, and the card says so on its face rather than failing at compile.

Live xAPI console

The statement, while you preview

Every signal a preview run would send, as the actual statement: real verb and object IRIs, the result object as it ships. Docked beside the course while editing, and absent from every export.

Redirect messages

Nobody is moved in silence

A reset, jump or reveal can carry a message. Being moved with no explanation reads as a broken course, so the wording is an argument to the call rather than an afterthought.

Undo that behaves

Coalesced, not forty steps

Rapid edits to one target collapse into a single entry, so typing does not leave you undoing letter by letter.

The library

Sixteen blocks, every one of them addressable.

Each block carries a ref, a progression rule, an entrance animation and an optional override. Tabs can hold other blocks, one level deep, which is enough for layout and not enough to lose track of.

Layout
≡
Accordion
Expandable sections. Report on one row or on all of them.
Layout
◫
Tabs
Tabbed panels, and the only block that can contain others.
Layout
↕
Timeline
Dated entries, vertical or horizontal.
Layout
⊞
Card Grid
Flip or click-to-open cards, two to four columns.
Layout
◈
Callout
Info, tip, warning, danger, success. Each one themed separately.
Layout
─
Divider
A rule, optionally labelled.
Media
▶
Video
Externally streamed, with questions on the timeline and a choice of what counts as finished.
Media
⊡
Image Block
Paired text and image, four layouts.
Media
</>
Code Sample
Read-only highlighted code with line emphasis.
Interaction
⇄
Matching
Pairs, with distractors, penalties per change, and a report on how much reworking it took.
Interaction
✦
Question Check
Single answer, immediate feedback, retry rules.
Interaction
◉
Reveal
Content held back until the learner asks for it.
Interaction
{;}
Code Exercise
Monaco, twenty accepted answers, requirements, or your own test service.
Layout
</>
Custom HTML
Sandboxed markup for the interface no stock block covers.
Feedback
★
Star Rating
Renders a score you supply, animated.
Feedback
▬
Progress Bar
Themed progress against any value you compute.

Before it ships

Check it as a learner, then check it as a machine.

Preview is the learner view under the constraints the export will meet, in a desktop, tablet or phone frame. Keep the progress from your last run while you edit, or switch on review mode to walk a long course end to end with every gate bypassed. Review mode is not a field on a block, is never written to a project file and the compiler has never heard of it, so no course can ship with its gates open.

  • VerificationThree stages. Schema and structure first, then a parse of every override with the verb allowlist checked — both local, both fast enough to run on every save. The third drives the compiled package in a headless browser to confirm the gates behave as authored.
  • SnapshotsRollback points stored beside the project, browsable and restorable.
  • Project fileOne .forge5p text file per project: content, theme and embedded assets in a delimited envelope, opened and saved locally.
  • Version controlThe format was built for it. Serialisation is canonical — object keys sorted, arrays ordered by explicit position, no timestamps in the checksummed payload — and volatile metadata sits in the envelope header, outside the body. The same project produces byte-identical output every time, so a commit appears only when the project actually changed. It is a single text file, so it commits, diffs and merges as text; the diff is base64, so you see that something changed rather than a readable line-by-line of what.
  • Not encryptionThe default envelope is canonical JSON, deflated, then base64url-encoded. That is obfuscation: it stops anyone hand-editing the file and makes corruption fail a checksum rather than half-load, but two lines will decode and inflate it. Treat a plain .forge5p as readable by anyone holding it.
  • Encrypted archivesserializeEncrypted() is AES-256-GCM with a PBKDF2-derived key, a fresh salt and nonce per export, and an auth tag — for handing a file to someone who should not be able to read it. Don't version-control that form: the random salt and IV mean the same project encrypts to different bytes every time, and the ciphertext will not compress, so the repo accumulates opaque blobs with no diff, no merge and no blame. Commit the plain archive and keep the repository private.
Verification
38 blocks · 3 stages
✓ stage 1 · structure and refs
✓ stage 2 · overrides parse, verbs allowed
! chapter 4 · gate can never open
score_threshold 0.9, max score 0.75
✓ stage 3 · gates behave as authored
Devices
Desktop · Tablet · Phone frames
Preserve learner progress across edits
Review mode, gates bypassed
Entrance animations, reduced-motion aware

Export

H5P where you can have it, SCORM where you cannot.

The package is a zip of content and code in an open format. Anything that supports H5P can serve, edit or extract it, and nothing downstream needs this editor.

.h5p

Full detail, per block

Moodle and anything else that reads H5P receives every statement with its verb and result, per-block scores kept separate, completion for each block and for the course, and whatever your overrides emitted. A report can name the block a learner stalled on.

SCORM 1.2 · 2004

One score, honestly labelled

A bridge translates the course's own xAPI into CMI calls. SCORM has a fixed vocabulary, so per-block detail collapses into a rolled-up score, a completion status and a pass or fail. Use it when the LMS cannot take H5P, not by preference.

Documentation

In the app, beside the work

Getting started, writing an xAPI override, custom HTML blocks and code exercises, opened from the toolbar. The worked examples are run by the test suite rather than proofread, because an example that has drifted out of correctness is worse than none.

Who this is for

Two people, usually. Sometimes the same person on different days.

Instructional and learning experience designers

You already argue about what completion should mean. Here that argument has somewhere to live: a short rule attached to a block, separate from the content, different for the client who wants it different. You can read a model-generated interaction well enough to wire it up, and you want the course to answer back while a learner is still in it.

Read the designer's route →

Front-end developers and designers

Monaco, an open package format, sandboxed opaque-origin frames, a theme layer built on custom properties and classes, canonical serialisation that makes an unchanged project export byte for byte, a single text file that commits and merges as text, and override parsing that runs before export. Course tooling that behaves like tooling.

Read the developer's route →

If your courses are readings and quizzes, this is overhead with no return. It earns its keep when the training needs an interface that does not exist yet, and needs that interface to be assessed rather than watched. The longer version of the thinking behind all of this — Peirce, Dewey, Forrester, Ackoff, Schön — is on the Ground & Form philosophy page.

Your LMS will keep telling you they finished. Everything else is yours to instrument.

Pilot phase

Try it on a real course.

F4xAPI Studio is in pilot. The download is the trial: author, preview, theme, write overrides and work through the built-in course, all of it offline. Activating a licence unlocks H5P and SCORM export. Tell us what breaks.

Download the x64 Windows installer
Save these for access:
default user · "admin"
default password · "chester345"
Windows · Linux · macOS coming Build · 11 September 2026 Offline authoring

For an export licence, email tigerteam@groundandform.com and say a little about the course you want to ship.

Supported Windows 10 and 11, x64. The download above is a signed MSI, installed per-machine, with the file association for .forge5p.
Supported Linux: Ubuntu 22.04 and later, Debian 12 and later, and Fedora generally. Ask for a build and say which distribution.
Not supported The RHEL 9 family: RHEL 9, Oracle Linux 9, Rocky Linux 9, AlmaLinux 9 and CentOS Stream 9.
Untested Amazon Linux 2023, and RHEL 10 — which should take minimal effort, but nobody has run it yet.
Coming macOS, pending Apple Developer Program validation and code signing. macOS refuses to install distributed software that is not signed, so the build waits on the certificate rather than on the code.

Changelog

What changed in this build.

Pilot build11 September 2026
  • Windows OV code signing on the installer and executable.
  • Licence server active for activation, validation and export licences.
  • Direct Moodle integration and Lumi tested.
  • Linux packaging for Ubuntu 22.04+, Debian 12+ and Fedora.
  • Modules in the authoring environment. Compilation and preview split per module, each into its own zip archive.
  • Score tracking and debugging in the authoring environment.
  • Smaller upgrades and fixes throughout.