---
name: spintax-engines
description: Install and call an open-source spintax engine from JavaScript, PHP, Python or Object Pascal — @spintax/core on npm, spintax/core on Packagist, spintax-core on PyPI, and a Free Pascal engine on GitHub. Use when adding spintax rendering or validation to an application, choosing between the engines, or wiring a template pipeline into a runtime.
---

# Spintax engines

Four MIT-licensed, zero-dependency engines implement the same syntax and are held to a
shared corpus of golden fixtures. Pick by runtime, not by feature list.

| Runtime | Package | Install |
|---|---|---|
| JavaScript / TypeScript | `@spintax/core` | `npm install @spintax/core` |
| PHP 8.0+ | `spintax/core` | `composer require spintax/core` |
| Python 3.10+ | `spintax-core` | `pip install spintax-core` |
| Object Pascal / Free Pascal 3.2.2+ | the `spintax-win` repo | `git clone` (no package registry) |

There is also a GPL-2.0 WordPress plugin, which embeds the PHP engine and adds an editor,
caching and field bindings. The engines above are the engine on its own.

The four are independent implementations, not ports of one another: JavaScript is the
reference engine and the home of the corpus, PHP is an extraction of the plugin's own
engine by the same copyright holder, and Python and Pascal are independent reimplementations.
Overview: <https://spintax.net/spintax-engines.md>

## JavaScript

One `render()` call runs the entire pipeline — comments, `#set`/`#def`, conditionals,
variables, plurals, enumerations, permutations, post-processing. Do not compose the stages
by hand; there is no stage-composition API.

```js
import { render, validate } from '@spintax/core';

const out = render(template, { seed: 42, locale: 'ru', postProcess: true });

const diagnostics = validate(template, { locale: 'ru' });
// Diagnostic[] — each with severity, code, message and a position.
// Valid ⇔ no entry with severity 'error'.
```

`seed` makes a render reproducible; omit it for fresh randomness. `locale` selects plural
arity. `render()` is total on template content — a malformed construct degrades visibly in
the output rather than throwing, which is why you validate separately when you need to
report errors. It throws `SpintaxError` only on programmer error.

The package also exposes `parse`, `analyze` and `neutralize`, plus `pluralArity` and
`normalizeBaseLang`. Full API: <https://spintax.net/spintax-for-javascript.md>

## PHP

```php
use Spintax\Core\Render\Pipeline;

echo $pipeline->render($raw, $runtimeVars, $context, $locale, $postProcess);
```

`Validator::validate($template, $knownSlugs, $globalVarNames, $locale)` returns
`['errors' => [...], 'warnings' => [...]]` with message, line and column.

Two things the PHP package deliberately does **not** have: no AST (so no `analyze` /
`neutralize`), and no `seed` — you inject the RNG yourself. Note also that `Plurals::apply()`
on its own is **strict** and throws on a broken construct; `Pipeline` opts into lenient mode
for you, which is why `render()` does not throw.
Full API: <https://spintax.net/spintax-for-php.md>

## Python

```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!"
```

`spintax-core` is an independent implementation rather than a port, kept behaviourally
identical by the shared golden-fixture corpus. Two traps: **post-processing is on by
default** and capitalizes sentence starts (pass `post_process=False` for the raw pick),
and the bare `spintax` name on PyPI is a different, unmaintained GPLv3 package — install
`spintax-core`.

## Object Pascal / Free Pascal

No package registry — clone <https://github.com/investblog/spintax-win> and add the unit.

```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 injected, not seeded: leave `ctx.Rng` nil for random output, or inject a
strategy (`TFirstRng`, `TLastRng`, `TSequenceRng`, seeded `TMulberry32Rng`). Free Pascal
3.2.2+ in `{$mode delphi}`; passes 168 of the corpus's 172 cases (the 4 skipped are the
engine-private RNG assertions).

## Cross-engine expectations

- **Identical output is guaranteed by the corpus, not by shared code.** If two engines
  disagree on a fixture, that is a bug worth reporting, not a documented difference.
- **The same seed does not mean the same output across engines.** Reproducibility is
  per-engine; only the set of legal outputs is shared.
- **Diagnostic codes are engine-specific.** Map them explicitly rather than falling through
  to a generic error — new codes appear on minor version bumps and silently degrade
  error reporting when unmapped.
- **A caret on a 0.x minor does not cross minors.** `^0.3.0` will not pick up `0.4.0`; bump
  the range deliberately when tracking a pre-1.0 engine.

## Related

- `spintax-syntax` — what the templates you pass in may contain
- `spintax-authoring` — designing those templates in the first place
- Browser playground, running `@spintax/core` client-side: <https://spintax.net/play/>
