HTML-driven native apps in Free Pascal — one codebase, six targets, no browser, no widget toolkit.
Tina4Pascal is the Free Pascal sibling of Tina4Delphi. It takes the same idea — the UI is HTML + CSS, the app is an event loop — and compiles it to a single ~1.3 MB native binary per target (macOS, Windows, Linux x64/arm64, Android, iOS) that carries the whole engine and no runtime. See App sizes.
![]() Windows · Win32 + GDI+ |
![]() Android · JNI → Canvas (built from Windows) |
One HTML file — showcase.html — byte-identical, drawn by the native engine on
each platform. The Lottie dino is a Bodymovin animation rendered by the
pure-Pascal core (no Skia, no JS); the same page lays out <video>,
gradients, transform, position:fixed and form controls. The CSS engine even
digests the real bootstrap.min.css.
# Windows (PowerShell)
irm https://raw.githubusercontent.com/tina4stack/tina4pascal/main/scripts/install.ps1 | iex# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/tina4stack/tina4pascal/main/scripts/install.sh | shThat puts tina4pascal on your PATH. FPC is fetched on your first init, so a
clean machine is ready in about two minutes.
Install (above), then one command:
tina4pascal init helloIt scaffolds the project, fetches FPC if you don't have it, builds, and opens a native window showing Hello World! with the logo — your first app in about two minutes, same command on every OS. No FPC flags, nothing to install by hand.
Then keep going:
cd hello
tina4pascal run # rebuild & run after you edit src/templates + src/routes
tina4pascal build android # ship an APK (arm64 + armv7 + x86_64), signed
tina4pascal doctor # see the whole toolchainOne of these is Chrome. The other is a ~1.4 MB native binary with no browser,
no WebView, no HTML/CSS engine but its own — the same kitchen-sink.html,
rendered by pure Free Pascal. Which is which?
![]() A |
![]() B |
Reveal
A is Tina4Pascal (native Free Pascal); B is Chrome. Gradients, the
web-loaded photo, bold/italic/monospace type, linear + radial swatches,
gradient/solid/outline buttons, the <select>, the checkbox, the
font-weight:800 heading and the whole card layout all match. The only tells
are the platform form widgets: the <input type="date"> (Tina4 formats it
06 Sep 2026 with a calendar glyph; Chrome shows the OS 2026/09/06 spinner)
and the <select> chevron — Tina4 draws both itself, so they follow your
CSS, not the OS. Reproduce it with tools/tina4pascal render beside any
browser.
And because Tina4 owns the widget, opening that date field gives you a full native calendar — engine-drawn, CSS-styled, identical on every platform (no OS date dialog):
And it goes the other way too — native widgets a plain HTML renderer can't do,
with no <script>, no CDN, no JS runtime:
<!-- a real, scannable QR — generated and drawn by the engine -->
<qrcode value="https://github.com/tina4stack/tina4pascal" width="180"></qrcode>
<!-- a Bodymovin animation, animated by the pure-Pascal core (no Skia, no lottie.js) -->
<lottie width="280" height="280">{ "v":"5.5.2", "fr":60, ...bodymovin JSON... }</lottie>
<!-- inline SVG: paths, shapes, strokes -->
<svg viewBox="0 0 100 100"><path d="M30 52 L45 67 L72 34" stroke="#fff" .../></svg>The <qrcode> and <lottie> tags are part of the engine — the same markup
renders on every target. See examples/pages/native-widgets.html.
Everything you drive the stack with — by hand, from an IDE, or from an AI agent.
| Tool | Where | What it does |
|---|---|---|
tools/tina4pascal |
macOS / Linux (POSIX sh) | setup · doctor · init · build · run · render · dom · boxes · inspect · debug · script · deploy · screenshot · compliance |
tools/tina4pascal.ps1 |
Windows (PowerShell) | same surface, native to Windows; build {win64,win32,linux,android,all}, setup android, doctor |
tools/mcp |
any MCP client (tina4-python) | MCP server — exposes the whole loop (tina4_init/build/run/render/dom/boxes/inspect/script/debug/deploy/screenshot) so any agent or IDE drives it. Tool-agnostic quick-start in tools/mcp/README.md |
skills/tina4pascal-developer |
any skill-aware agent | agent skill: architecture rules, toolchain formula, verification discipline (./scripts/install-skills.sh; presets for Claude Code · Codex · Cursor) |
toolchain/build-crosses.sh |
macOS / Linux | build every FPC cross-compiler from one host |
toolchain/build-android-cross.ps1 |
Windows | build the FPC→Android cross pack (arm64/armv7/x86_64) from source + NDK |
toolchain/sign-release.ps1 |
Windows | EV-sign the pack binaries + CLI via SimplySign (docs/SIGNING.md) |
doctor on either CLI reports the whole chain and tells you exactly what's
missing and how to fix it.
The whole engine — HTML parser, CSS cascade, layout, compositor, SVG, QR, Lottie, video, form controls — is inside every binary, reached by runtime tag dispatch, so a feature-complete app is the same size as hello: the full viewer is 1.38 MB vs hello's 1.31 MB (67 KB apart). Features live in the engine, not your app.
Release builds, whole engine reachable:
| Target | Artifact | On disk | In package |
|---|---|---|---|
| Windows x64 | .exe |
1.31–1.38 MB | — |
| Android arm64-v8a | libtina4.so |
1.57 MB | 413 KB (compressed) |
| Android armeabi-v7a | libtina4.so |
1.23 MB | 389 KB |
| Android x86_64 | libtina4.so |
1.40 MB | 415 KB |
| Android APK | all three ABIs | 5.96 MB¹ | — |
| macOS / iOS | .app / engine |
~1.4 MB² | — |
¹ dominated by launcher-icon art at every density; a resize-on-package step (tracked) drops it back to ~1.5 MB. The engine payload is ~1.2 MB across all three ABIs. ² same engine; measure on a Mac.
| Manual | |
|---|---|
| Getting started | zero to a running native app |
| Cheatsheet | the tag / CSS surface + action & services APIs |
| Architecture | portable core, per-OS shells, compositor |
| Toolchain | the FPC 3.2.2 cross-build formula (every pitfall) |
| Android · android/README.md | the JNI shell + building APKs (incl. from Windows) |
| iOS | the on-device iOS shell |
| Signing | EV code signing the Windows deliverables (SimplySign) |
| Tooling & distribution | packaging & release model |
| Conformance · CSS index · HTML index | what renders, tracked against the spec |
| Roadmap | where it's going |
No TButton. No components. You hand the renderer HTML (from a Frond/Twig
template, an API, or a string); interaction comes back as semantic events:
Render.SetHTML(html);
Render.OnElementClick(obj, method, params); // onclick="Cart:add('sku42')"
Render.OnFormSubmit(formName, fields);
Render.OnLinkClick(url, handled);Because state lives in the DOM and interaction is events, the same app runs under a headless backend — a GUI that is scriptable and testable by construction. See docs/ARCHITECTURE.md for the layering (portable DOM/CSS/layout core, one small shell per OS, software rasterizer as the road to pixel-identical output everywhere).
src/— the portable core + one shell per OSTina4HTMLDom.pas— DOM, HTML parser, CSS selector engine, computed styles (ported line-for-line from Tina4Delphi'sTina4HTMLRender.pas; 126-assertion test suite intests/)Tina4HTMLLayout.pas— block/inline/table layout + painting via the canvas contractTina4RenderBackend.pas— the platform contract (canvas, window, events, images) + the portable software rasterizerTina4ShellCocoa.pas(macOS/iOS, Objective-Pascal),Tina4ShellWin.pas(Win32 + GDI+),Tina4ShellLinux.pas(Xlib),Tina4ShellAndroid.pas(JNI → Canvas) — no Lazarus/LCL, no Qt/GTKTina4App.pas— the one cross-platform hostRunAppdrives
tools/— thetina4pascalCLIs + the MCP server (see Toolsets)toolchain/— build the FPC cross-compilers on any host (build-crosses.shfor macOS/Linux,build-android-cross.ps1for Windows)docs/TOOLCHAIN.md— the formula: every pitfall of building the FPC 3.2.2 cross toolchain, with fixes
The tina4pascal CLI above is the normal path. To build the low-level viewer
and test suite directly:
# toolchain: see docs/TOOLCHAIN.md (brew fpc bootstrap → ~/fpc with crosses)
cd examples/htmlviewer
mkdir -p csscache && curl -sL -o csscache/bootstrap.min.css \
https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css
PPC_CONFIG_PATH=$HOME/fpc/etc $HOME/fpc/bin/fpc -Mdelphi -Fu../../src htmlviewer.pas
./htmlviewer # renders bootstrap_test.html; clicks print events
./htmlviewer --snapshot out.png # self-screenshot (seed of headless mode)Run the DOM/CSS test suite:
cd tests && PPC_CONFIG_PATH=$HOME/fpc/etc $HOME/fpc/bin/fpc -Mdelphi -Fu../src test_dom.pas && ./test_domThe Windows shell is Win32 + GDI+ with DIB-section offscreen compositing —
so filter, mix-blend-mode, mask-image, 3D transform and backdrop-filter
all render, sharing the same Tina4Compositor as macOS/iOS.
:: 1. Install FPC 3.2.2 — the official installer from https://www.freepascal.org/download.html
:: (puts fpc.exe on PATH; no extra config, unlike the Mac's self-contained ~/fpc)
:: 2. Build + run the viewer (fpcres embeds the app icon; ships with FPC):
cd examples\htmlviewer
fpcres htmlviewer_win.rc -o htmlviewer_win.res -of res
fpc -Mdelphi -Fu..\..\src htmlviewer_win.pas
htmlviewer_win.exe win-test.html :: any .html; no arg loads the built-in @demotina4pascal build win64 does the fpcres step for you. The source embeds the
precompiled htmlviewer_win.res (not the .rc) because the cross-toolchain has
no x86_64-win64-windres — building straight from the .rc silently dropped the
app icon.
Cross-compiling the .exe from macOS/Linux instead:
$HOME/fpc/bin/fpcres examples/htmlviewer/htmlviewer_win.rc \
-o examples/htmlviewer/htmlviewer_win.res -of res
PPC_CONFIG_PATH=$HOME/fpc/etc $HOME/fpc/bin/fpc -Mdelphi -Twin64 -Px86_64 \
-FE/tmp/w -FU/tmp/w -Fusrc examples/htmlviewer/htmlviewer_win.pasCode signing (Authenticode). tina4pascal build win64 signs the .exe with
osslsigncode straight from the Mac cross-build (no Windows box needed), SHA-256 +
RFC3161-timestamped. Two key sources, tried in order:
- SimplySign / a PKCS#11 token (Certum cloud EV cert — the key never leaves the
token). Auto-used when SimplySign is installed; just log into SimplySign Desktop
first, then
tina4pascal build win64— no env vars needed. Overridable withTINA4_WIN_PKCS11_MODULE/_ENGINE/_CERT/_KEY(defaults autodetect the module + libp11 engine and pick the token's single cert/key). - A PKCS#12 file:
export TINA4_WIN_CERT=/path/to/codesign.p12 # .pfx/.p12 export TINA4_WIN_CERT_PASS=… # optional
Common overrides: TINA4_WIN_TS (TSA URL, default DigiCert), TINA4_WIN_SIGN_NAME,
TINA4_WIN_SIGN_URL. With neither key source available the build prints an
UNSIGNED notice and proceeds, so dev builds still work. Needs
brew install osslsigncode libp11 (already on the build Mac); secrets stay in the
environment / token, never the repo.
The GDI+ shell renders shapes, ClearType text, clipping, clip-path, 2D
transforms, the full compositing pipeline, AA gradients (linear/radial),
<img> decode/draw, per-pixel rgba() alpha and background-size: cover —
the comparison above exercises them. Reftests: 130/130
pass. What's left:
- Advanced blend modes —
color-dodge/burn,hue/saturation/color/luminosityfall back to source-over inBlendPixel(multiply, screen, darken, lighten, overlay, difference are in). - HiDPI — the shell runs at density 1; read the per-monitor DPI and supersample the layers.
clip-pathunder a transform — the GDI clip is device-space, so a clip-path combined with rotate/scale isn't yet positioned correctly.
See src/Tina4ShellWin.pas (canvas) and examples/htmlviewer/htmlviewer_win.pas
(the Win32 host + message loop).
Frond (templates) ─▶ Tina4HTMLDom + Tina4HTMLLayout (render) ─▶ platform shell (native window)
▲ │
└────── data layer: REST · SSE · WebSockets ◀── events (click/submit) ─┘
One HTML-driven model, six targets from one machine (macOS, Windows or Linux). Each layer has a design doc:
| Layer | Status | Doc |
|---|---|---|
| Renderer (DOM/CSS/layout) | rendering on macOS, Windows (130/130 reftests) and Linux; systematic conformance push | CONFORMANCE.md, CSS-PROPERTY-INDEX.md, HTML-ELEMENT-INDEX.md |
| Platform shells | macOS + iOS (Objective-Pascal), Windows (GDI+), Linux (Xlib) and Android (JNI → Canvas) all render; desktop shells share offscreen filter/blend/mask/3D compositing. Android APKs build natively from Windows | ARCHITECTURE.md |
| Data layer (WS/SSE/API) | designed, ports of tina4delphi units | ROADMAP-DATALAYER.md |
| Frond template engine | designed, port of Tina4Frond.pas | ROADMAP-FROND.md |
W3C-style reftests (each feature: a test render vs a known-good reference; pass when they match, Chrome validates the test):
tools/run-compliance.sh # reftest suite → PASS/FAIL table
tools/compare.sh bootstrap_test # stack our render over Chrome for a pageSigned downloads (Windows Authenticode + Linux/macOS GPG) are on the
GitHub Releases page; each
asset ships a .sha256. The macOS build is GPG-signed rather than Apple-notarized,
so a browser download may be quarantined — verify it, then
xattr -dr com.apple.quarantine <binary> to clear the Gatekeeper flag.
A big feature release — three new subsystems, all pure-Pascal and cross-platform.
- ThreePascal — a software 3D engine — a pure-Pascal three.js-style renderer (scene/camera/mesh, textures, AnimationMixer, GLTFLoader, Raycaster, instancing, FXAA, culling) that rasterises entirely on the CPU with zero GPU dependency, so it runs identically everywhere. Ships with the walking-Merino-ram demos and a 20-strong flock. Desktop hosts for macOS, Windows and Linux plus on-device Android/iOS — the same app source on all.
- Barcode scanning — a native
<barcode-scanner>element withonscandispatch: a live camera preview with an HTML overlay on Android and iOS, plus a desktop/image decoder (zbar) and a scan ViewScreen example. - SSO with RBAC — generic OIDC/OAuth 2.0 login (Authorization Code + PKCE),
JWT claims, and role/permission access control expressed declaratively in HTML
(
data-role/data-perm). The desktop flow is complete: PKCE crypto, loopback redirect capture (can't hang), token exchange, and session storage encrypted at rest with DPAPI on Windows. Seedocs/SSO.md. - Standalone, branded desktop apps — apps embed their UI and build to a single
exe with no external assets, no console window (GUI subsystem), and an embedded
icon shown in Explorer, the title bar and the taskbar.
tina4 initscaffolds it all; a calculator example demonstrates it desktop + Android from one HTML source. - Rendering — the frame now fills the whole window with the page's own root/ body background (canvas-background propagation); multiple, inset and soft-blurred box-shadows; border-radius on images, backgrounds and overflow; dashed/dotted/ double outlines; percentage heights resolved against a definite container.
- macOS notarization —
release-macos.shnow Developer-ID-codesigns (hardened runtime) and notarizes the macOS binary when a notary credential is configured, so downloads open without a Gatekeeper warning. - Windows clean-box installer hardened — the FPC auto-install validates the download is a real installer (browser UA, direct mirror, PE-header + size check) before running it, instead of ever executing a corrupt/partial file.
- CI — the Windows job installs FPC reliably (no longer gated on SourceForge rate-limiting CI runners); Linux/macOS/Windows all green.
- Bundled fonts on every platform — drop
.ttffiles in a project'sfonts/folder and the app ships and renders with them identically on Linux, Windows, macOS, Android and iOS.initscaffolds the folder;buildbundles it; the renderer registers the fonts with each OS text engine and resolves them bundled-first (genericsans-serif/serif/monospace→ the bundled DejaVu family when present, else the system font). Emptyfonts/→ system fonts. - iOS —
deploy iosfixed andCFBundleIdentifierparameterised so device builds sign + install. - macOS downloads are GPG-signed (not notarized); clear Gatekeeper with
xattr -dr com.apple.quarantine <binary>after verifying.
- WebP — pure-Pascal decoder (VP8L lossless, lossy VP8, lossy + ALPH alpha), wired into the image pipeline. No external libraries.
- HTML → PDF — a headless
Tina4CanvasPdfrenders the same layout to a FlateDecode-compressed PDF (--pdf), with embedded images and text. - Linux text via FreeType — scalable, Unicode, anti-aliased. The old X11
core-font path only reached 8-bit iso8859-1, so glyphs like
−,—,…,↔rendered as tofu; they render correctly now. Bundled fonts: afonts/directory beside the executable wins over system fonts, so an app renders identically everywhere (including minimal containers). - DrawRGBA on every shell (Windows / Cocoa / Linux) — on-screen WebP and faster Lottie.
- Fix — inline
margin-rightwas dropped by the layout engine, so inline-block elements sat flush against the next box (a chip strip touched instead of spacing out). Now honoured for inline-block,<img>, form controls and<svg>on every backend. - Tests / CI — new
webpandpdfsuites wired intotina4pascal test(12 total); a cross-OS matrix builds/tests on every push and attaches the Linux + macOS artifacts when a release is tagged.
First release — the native HTML/CSS engine on Windows (GDI+), Linux (X11), macOS (Cocoa) and Android (JNI), with the developer CLIs, the AI-drivable MCP server, and signed Windows + FPC→Android cross-pack artifacts.
- Conformance: add the advanced blend modes and HiDPI (last Windows gaps), then keep pushing flexbox, positioning, list markers and table spans.
- Linux shell parity: images + the filter/blend/mask/3D compositor (the desktop-class pieces the X11 shell doesn't share yet).
- Data layer: dispatcher → DOM-mutation API → REST → SSE → WebSockets (ports of the Tina4Delphi units) — live, data-aware native apps.
- Frond: port the template engine — templates + data → HTML.
- Keep the agent skills current so AI coders drive the stack without rediscovering the pitfalls.
The repo ships an agent skill (skills/tina4pascal-developer/) that teaches any
skill-aware agent the architecture rules, the toolchain formula, and the
verification discipline for this stack. Presets install it into the common
tools:
./scripts/install-skills.sh # all presets
./scripts/install-skills.sh claude # or: codex, cursorTo drive the stack live from any agent or IDE, run the tool-agnostic MCP server in tools/mcp/ — one endpoint, copy-paste client setup for Claude Code, Cursor, Windsurf, VS Code, Zed, Codex and more.
Moving fast. macOS, Windows, Linux and Android render natively today (see the comparison and the size table); iOS runs on-device. Android APKs build from a Windows host with no Mac and no WSL. Next: the data layer (REST/SSE/WebSockets) and the Frond template engine.
MIT licensed, part of the Tina4 stack.





