# Spintax engines One spintax syntax, separate engines for JavaScript, PHP, Python, Object Pascal and .NET. Each is an independent implementation, not a port of the others, and every one of them is held to the _same_ corpus of golden fixtures so a template renders the same wherever you run it. MIT-licensed, zero dependencies, no external service in the loop. ## One syntax, five runtimes The [spintax syntax](https://spintax.net/docs/syntax.md) documented on this site is a superset of the flat `{a|b|c}` that most tools stop at. It adds permutations, scoped variables, value-driven conditionals, locale-aware plural agreement, includes and a post-processing pass. That superset is not tied to one language: five engines implement it, and you pick by the runtime you already have. | Runtime | Package | Install | License | | --- | --- | --- | --- | | JavaScript / TypeScript | `@spintax/core` | `npm install @spintax/core` | MIT | | PHP 8.0+ | `spintax/core` | `composer require spintax/core` | MIT | | Python 3.10+ | `spintax-core` | `pip install spintax-core` | MIT | | Object Pascal / Free Pascal | the `spintax-win` repository | `git clone` (no package registry) | MIT | | .NET (netstandard2.0, net472) | `Spintax.Core` | `dotnet add package Spintax.Core` | MIT | All of them are zero-dependency. The GPL-2.0 WordPress plugin embeds the PHP engine and adds an editor, caching and field bindings on top; the packages above are the engine on its own, with no assumptions about where your templates live. ## Not ports — independent implementations held to one corpus "The same syntax in five languages" is easy to claim and hard to keep true. Two hand-written parsers drift the moment one fixes an edge case the other has not seen. What holds this family together is not shared code — there is almost none — but a shared **golden corpus**: a set of language-neutral fixtures, each an input template plus the exact output, diagnostics or extraction it must produce. Every engine's test suite loads that same corpus and asserts against it. A case that passes in TypeScript and fails in Pascal is a bug in Pascal, caught before release, not a difference users discover in production. The corpus is machine-checked parity, not a promise in a README. - **The corpus lives with the reference engine.** `@spintax/core` is where new fixtures are written; the PHP, Python, Pascal and .NET suites consume the exact same JSON. - **Semantics are gated, randomness is not.** The fixtures pin what a construct _means_ — which options are valid, how a plural agrees, what a conditional selects. They deliberately do not pin the random pick: seeded output is reproducible _within_ one engine, and identical random sequences _across_ engines is a non-goal. Those cases are engine-private and skipped on purpose. The practical payoff: you can author a template against the playground on this site — which runs the JavaScript engine — and trust that the PHP job, the Python script, the Pascal binary or the .NET automation host that renders it in production will read it the same way. ## Pick by runtime ### JavaScript & TypeScript — `@spintax/core` The reference engine, and the home of the corpus. Zero runtime dependencies, ESM-first with dual CJS, and it runs unchanged on Node 18+, Cloudflare Workers and in the browser — it is what powers the [playground](https://spintax.net/play/) on this site. ```js import { render, validate, extract } from '@spintax/core'; render('{Hello|Hi} %name%!', { context: { name: 'Ada' }, seed: 42 }); // deterministic for a given seed; post-processed by default ``` One `render()` call runs the whole pipeline. It carries the richest tooling of the family — a reusable AST from `parse`, plus `analyze` and `neutralize` — and its diagnostics carry a stable, parity-gated `code`, which is what lets the playground map an error to a translated message. The [full JavaScript guide](https://spintax.net/spintax-for-javascript.md) covers the API surface in depth. ### PHP — `spintax/core` Framework-agnostic, PHP 8.0+, `ext-mbstring`, nothing else. Unlike the others, this package is not an independent reimplementation: it is an **extraction of the WordPress plugin's own engine** by the same copyright holder, relicensed MIT so any PHP app can use it while the plugin stays GPL. ``` use Spintax\Core\Pipeline; $pipeline = new Pipeline(); echo $pipeline->render('{Hello|Hi} %name%!', ['name' => 'Ada']); ``` There is no `seed` argument — you inject determinism by constructing the pipeline with your own selector — and no stable diagnostic `code`; the validator reports a message with a line and column, and a host branches on structure. The [full PHP guide](https://spintax.net/spintax-for-php.md) covers what the package does and deliberately does not carry. ### Python — `spintax-core` Python 3.10+, an independent implementation held to the corpus. The API is snake\_case and reads the way Python expects: `render(template, *, context=, seed=, post_process=)`. ```python from spintax_core import render, validate, parse render("{Hello|Hi} there!", seed=42) # same seed, same output render("Hi %name%!", context={"name": "Sam"}) # "Hi Sam!" ``` ### Two traps worth knowing before you `pip install` **Post-processing is on by default** and capitalizes sentence starts, so `render("{fast|cheap} hosting")` comes back "Fast hosting" even though the option is lower-case. Pass `post_process=False` when you want the raw pick. **The name `spintax` on PyPI is a different, older package** — GPLv3 and unmaintained since 2018. This engine is `spintax-core`; install that one. Source and full parity notes: [the `spintax-py` repository](https://github.com/investblog/spintax-py). ### Object Pascal / Free Pascal The fourth engine: a zero-dependency implementation in Object Pascal, built with Free Pascal 3.2.2+ in `{$mode delphi}`. It has no package registry — you clone the [repository](https://github.com/investblog/spintax-win) and add the unit to your program. ```pascal uses Spintax; var ctx: TSpContext; begin DefaultSystemCodePage := CP_UTF8; { declare UTF-8 once } ctx := Default(TSpContext); ctx.PostProcess := True; SpRender('{Hello|Hi} there!', ctx); end; ``` Determinism is a seam rather than a seed: leave `ctx.Rng` nil for non-deterministic output, or inject one of the shipped strategies — `TFirstRng`, `TLastRng`, `TSequenceRng`, or a seeded `TMulberry32Rng`. It implements the full superset and passes 254 of the corpus's 258 cases, the four skipped being the engine-private RNG assertions. The source also compiles unchanged under a UTF-16 Object Pascal compiler; that portability is kept, not maintained — a supported side effect, not a second platform. ### .NET — `Spintax.Core` The fifth engine, and the newest (NuGet, 2026-08-22): an independent implementation in C#, MIT, zero dependencies, built as `netstandard2.0` and `net472` from one source, so it runs on .NET Framework 4.7.2+ and on every .NET since. It exists because the .NET packages named for spintax implement the flat `{a|b}` dialect of the 2010s; this one implements the superset, and it is tested against the shared corpus rather than against its own expectations — **all 258 cases pass on both targets**, the four `kind:rng` ones included, driven through the engine's RNG seam. ```bash dotnet add package Spintax.Core ``` ```csharp using Spintax.Core; Engine.Render("{Hello|Hi} there!", new RenderOptions { Seed = "42" }); // same seed, same bytes foreach (var d in Engine.Validate("{a|b")) Console.WriteLine($"{d.Severity} {d.Line}:{d.Column} [{d.Code}] {d.Message}"); // Error 1:1 [bracket.unclosed] Unclosed '{'. Engine.Combinations("{a|b} {c|d|e}"); // 6 ``` The surface is deliberately flat — a static `Engine` with `Render`, `Validate`, `Extract`, `Analyze`, `Neutralize`; strings, dictionaries and small option objects, no `Task`, no mutable static state — because its first home is the kind of automation host that loads a dll by name and compiles snippets against it, ZennoPoster-style. That shape is what lets dozens of threads render with one seed and get identical bytes. Two calls the other engines do not have: `Combinations` counts the distinct texts a template can produce by walking the tree (`{a|a}` is 2; pass the variables and it counts for that one row of data), and `MaxLength` bounds the longest render. Rendering is lenient and post-processed by default; values in `Context` are re-parsed as templates, so data from a table goes through `Engine.Neutralize` first, as everywhere in the family. Source: [the `spintax-dotnet` repository](https://github.com/investblog/spintax-dotnet); package: [NuGet](https://www.nuget.org/packages/Spintax.Core). ## What they share, where they differ Every engine renders the same syntax and returns a valid pick for any well-formed template. Where they differ is ergonomics — how you reach for determinism, how much tooling ships around the core, and how diagnostics come back. | | JavaScript | PHP | Python | Pascal | .NET | | --- | --- | --- | --- | --- | --- | | Reproducible output | `seed` | injected selector | `seed` | injected `TSpRng` | `Seed` | | Post-processing default | on | off (opt-in arg) | **on** | off (zeroed record) | on | | Reusable AST | yes | yes | yes | yes | no (flat surface by design) | | Stable diagnostic code | yes | no | no | no | yes | | Exact variant count | no | no | no | no | `Combinations` | | Deep guide on this site | [yes](https://spintax.net/spintax-for-javascript.md) | [yes](https://spintax.net/spintax-for-php.md) | repo | repo | repo | What is gated across all five is the _verdict_ — whether a template is valid and what a construct renders to — not the wording used to explain a problem or the exact random pick. ## All MIT, so they compose The packages are MIT-licensed and depend on nothing. The WordPress plugin stays GPL-2.0, and MIT is GPL-compatible, so the GPL plugin can consume the MIT PHP engine while the reverse would not hold. Everything renders locally: no external service, no per-render call, no data leaving your runtime. ### Pairs naturally with LLMs Have a model draft the template _once_, then render unlimited variations on your own hardware — deterministic, free and offline, in whichever of the five runtimes you already ship. The model handles creativity; the engine handles scale. Curious what the "no per-render call" part is worth? See [what AI content actually costs](https://spintax.net/ai-content-costs.md). Coding agents get the condensed version: the [spintax-engines skill](https://spintax.net/.well-known/agent-skills/spintax-engines/SKILL.md) covers install and API for every engine, and templates can be validated over MCP — hosted at `https://spintax.net/mcp`, or on your own machine with `npx @spintax/mcp`, which drops the size caps and can read `#include` partials from disk. --- [Try in the playground](https://spintax.net/play/) [JavaScript guide](https://spintax.net/spintax-for-javascript.md) [PHP guide](https://spintax.net/spintax-for-php.md) [All repositories on GitHub](https://github.com/investblog) [Back to all guides](https://spintax.net/docs.md) --- Source: https://spintax.net/spintax-engines/