How Factorial works
Factorial runs the process-improve Python package inside your browser tab. The server sends the app's files and nothing more: your factors, results and analyses stay on your machine, unless you send them in a report with Feedback.
What runs where
Your browser tab
- The page (
index.htmland its JavaScript modules) draws the form, the run sheet, the tables and the charts, and holds the study you are working on. - A Web Worker runs Python. Pyodide is CPython compiled to
WebAssembly; in a worker, a long computation leaves the page free to scroll and type in. The page sends it
each request as JSON, and
web/bootstrap.pyanswers with the same process-improve functions you would call in Python:generate_design,compare_designs,TrueProcessandanalyze_experiment. A study of one factor also compares its levels with the package's tests:one_way_sample_sizesizes it;ttest_independent,ttest_paired,randomization_testandrank_sum_testcompare two levels; andstudentized_range_value(Tukey's intervals),dunnett_value,bartlett_testandkruskal_wallis_testcompare three or more. - The charts are process-improve's own Plotly figures, drawn in the page with Plotly.js.
- Your studies are saved in this browser's IndexedDB as you work, every one of them, and a brief not yet made into a run sheet is kept in its local storage. Clearing this site's data in the browser deletes them: the study file keeps a copy elsewhere.
- A link to a study holds the study itself, compressed, after the
#. A browser does not send that part of an address to any server: the link travels only where you paste it. - The workbook and the notebook (
.xlsxand.ipynb) are written in the tab and saved by your browser, as any download is.
The study file
Download all, under Your studies, saves every study as one JSON file, and Share beside a study sends that study alone, as a file of its own (it reads Download in a browser that cannot share a file). Drop the file on the dropzone under Your studies, in any browser: a file of one study opens it on the page, and a file of several adds them all. A study already kept in that browser is not saved twice, and none is overwritten.
The file is plain JSON, readable without Factorial (web/study-file.js writes and reads it):
{
"format": "factorial-studies",
"version": 1,
"exported": "2026-10-21T08:30:00.000Z",
"app": { "name": "Factorial", "url": "https://factori.al/", "process_improve": "1.4.0" },
"studies": [
{ "id": "8f1c…", "created": "…", "updated": "…", "sha256": "ab12…",
"study": { "v": 1, "title": "…", "design": { … }, "responses": [ … ], … } }
]
}
A study is whole: its brief, its design and run sheet, each response's results and where each came from, the
process they were simulated from, the models, and the settings kept from the what-if panel, each under its name,
as "what_if": [{ "name": "Cooler, more pressure", "settings": { "T": 175, "P": 2.5 } }]. Their
predictions are not kept: Learn works them out from the models and the results each time, so they follow a
result that changes. The file is built to grow with Factorial:
- New things are new keys. A reader skips a key it does not know, at any level, so a later Factorial adds what it needs beside what is there: a response measured as a curve, or a model inverted to find the settings for a target.
- Each study names its format,
"v". It goes up when a study holds something an earlier Factorial could not keep. That Factorial then refuses the study, and says so, rather than open part of it; the other studies in the file open. A study of one factor is format 2, a study with a categorical factor that lists its levels (three or more, or two that read as numbers) is format 3, a study of more than 12 factors is format 4, and a study with settings kept from the what-if panel is format 5; any other study keeps format 1, so an earlier Factorial still opens it. The file's own"version"goes up only for a change an earlier Factorial would misread. - A study keeps its
"id"through files and copies, so a later study can name the one it builds on: a design that follows another and its model, with factors added or dropped. - Each study carries the SHA-256 of its contents, so a study changed by hand is told apart from one as Factorial saved it. It still opens, and Factorial says so.
What reaches a server
- factori.al sends the app's files. It has no database, no accounts, no cookies and no
tracking script. Its web server writes one line per request, as web servers do: the time, the file asked for,
the result, the browser's name (its
User-Agent) and the page that linked here. Your address is cut to its network before the line is written (the first 24 bits of an IPv4 address, the first 48 of an IPv6 one), and the lines are kept for 90 days, to count visits. - jsDelivr and PyPI send Python and its packages, the first time and after a new version; your browser keeps them. They see an ordinary download, as for any web page that uses them, and nothing of your study.
- Feedback reaches factori.al only when you press Send in the Feedback dialog, and holds what the dialog lists under "What will be sent": the topic, your message, your email address if you give one, a screenshot of the tab unless you untick it, your study only if you tick it, and the page's build, versions, tab and last Python errors. The site's relay emails it to the maintainer's mailbox through the email provider that sends the site's mail, and keeps nothing of it, in a file or in its log. The dialog then shows the report's reference, such as FB-7K3Q2M. If you give an address, the relay also sends it a receipt: the reference, the topic and the day, and nothing you wrote. An address gets at most one receipt a day; to count them, the relay holds a keyed hash of each address, in memory only, for a day. A copy of Factorial at another address sends nothing itself. Read the code.
What this copy is built with
These are the versions this copy of Factorial loads, read from the files it loads them by:
runtime.json and wheels/manifest.json.
| process-improve | the wheel built with this copy |
|---|---|
| Python, in WebAssembly | Pyodide |
| From the Pyodide release | numpy, pandas, scipy, statsmodels and more |
| From PyPI | openpyxl and plotly |
| Charts | Plotly.js |
Read the code
All of it is open source, under the MIT license, at github.com/kgdunn/process-improve. To check what it does with your data, these are the places to read:
web/: the page, andworker.js, which starts Python;web/bootstrap.py: every call the page makes into Python;scripts/build_web_app.py: how this site is built from the repository;deploy/factorial/: the server at factori.al, its configuration and what it logs, and the relay that emails a report sent with Feedback;tests/web/: the tests that check the page, in a real browser and under Pyodide, on every change.
OMARS® is a registered trademark of KU Leuven Research & Development. Factorial uses the name only for the class of designs defined by Núñez Ares and Goos (2020), and is not affiliated with, or endorsed by, KU Leuven, Minitab or Effex.
Run it yourself
Factorial is a folder of plain files, so it runs on any web server. To build that folder from the code and serve it on your own machine (with uv installed):
git clone https://github.com/kgdunn/process-improve
cd process-improve
uv sync --all-extras
uv run python scripts/build_web_app.py --out site --serve
then open http://localhost:8000. The site folder can be copied to any web server:
nginx, Apache, Caddy, an internal server or GitHub Pages. It has to be served over HTTP, as Python's files are
fetched by the worker, so opening index.html from a disk does not work.
deploy/factorial/
holds the setup that serves factori.al, with deploys and roll-backs, as a worked example.
On a network without access to the internet, the browser has to fetch Pyodide, the two PyPI packages and
Plotly.js from inside it as well: copy them to a server there, and change their addresses in
web/worker.js (Pyodide and the PyPI packages) and web/charts.js (Plotly.js).