Random generator engine. Render `randomness` codeblocks inline with rich text output, plus support for legacy Inspiration Pad Pro 3 .ipt files.
An Obsidian plugin for IPP3-compatible random generators.
Roll on tables inline with `rdm:[@Names]`, embed full generators in
randomness codeblocks, and re-use existing .ipt files from twenty
years of the Inspiration Pad
ecosystem.
randomness codeblocks — embed a generator directly in a note. Rolls
every render; supports the full IPP3 grammar including weighted tables,
lookup tables, deck picks, conditionals, dice, expressions, and 21 filters.rdm: calls — one-shot rolls scattered through your prose.
Preview first, then click 🔒 to commit the result as
`rdm:[@Names]⟹Alice` — the lock survives reloads, syncs, and
reopening the vault. Click 🎲 to re-roll.Use: other files — share table libraries across notes. Reference
.ipt files or .md notes containing randomness codeblocks; resolution
follows the calling note's folder first, then a configurable generator root.Prompt: controls render dropdowns
or text inputs above the output; changing a value re-rolls with the new
prompt set..ipt file in your configured Generator root (or whole
vault) as a collapsible folder tree mirroring your vault's
structure. Click a folder or file chevron to expand; click any
table's Roll button to generate a result, or 📋 to copy
an inline rdm: reference for that table to your clipboard
(paste into prose; the Notice shows the Use: line you'll need
to add to your note). Click the result body to copy the rendered
text. The tree starts fully collapsed and remembers what you
expand across sessions. A "Collapse all" button resets the tree
without clearing your search filter, so collapse-then-filter is a
fast way to find a specific generator in a deep folder hierarchy.
Open via the dice ribbon icon or the "Open generator browser"
command..ipt files work as-is. The engine survives the full
AddCommas/Random-Treasure-CR1-CR30 stress test from the NBOS corpus.app.plugins.plugins["randomness"].api. Scoped and
unscoped rolls, prompt overrides, deterministic seeds, and a roll
event stream. Ideal for generating notes from a shared generator
library. See API.md.main.js, manifest.json (and styles.css if present) from the
latest release..obsidian/plugins/randomness/ inside your vault.```randomness
Table: Settlement
Riverbend
Stonewatch
Greenhollow
```
Renders to one of "Riverbend", "Stonewatch", or "Greenhollow", chosen at random each time the codeblock renders.
Use the full IPP3 grammar — multiple tables, weighted entries, lookup
tables, Set:/Define:, prompts, conditionals, dice expressions, filters,
the lot:
```randomness
Prompt: Tier {Easy|Normal|Hard}Normal
Table: Encounter
1: A single goblin scout.
2: [@Group] goblins.
6: A goblin chieftain with [1d4+2] {$prompt1} guards.
Table: Group
1-3: small group of {2d4}
4-6: warband of {3d6}
```
The dropdown for Tier appears above the result; changing it re-rolls.
rdm:Anywhere in your prose, wrap an expression in backticks with the rdm:
prefix:
The shopkeeper, a `rdm:[@Names]` from `rdm:[@Origin]`, eyed me suspiciously.
Each `rdm:...` renders inline with a preview, plus 🔒 (lock) and 🎲
(re-roll) buttons. Clicking 🔒 rewrites the underlying text to include the
chosen result:
The shopkeeper, a `rdm:[@Names]⟹Tessith Vone` from `rdm:[@Origin]⟹Coppertown`, eyed me suspiciously.
The lock survives reloads, sync, and reopening the vault. To re-roll a locked call, click 🎲 — it strips the lock and shows a fresh preview.
The expression's scope sees same-note randomness codeblocks plus any
Use: declarations from those blocks, so you can keep table definitions
alongside the prose that uses them.
In a shared .ipt file (e.g. Generators/common-names.ipt):
Table: Names
Tessith Vone
Korad the Blue
Mira Thornhaven
In any note's codeblock:
```randomness
Use:common-names.ipt
Table: NPC
{1d2=1, A man named, A woman named} [@Names].
```
Use: paths resolve relative to the current note's folder first, then
relative to the Generator root configured in Settings → Randomness.
rdm: in current note — evaluates every unfilled
inline call (using cached previews where available, fresh evaluations
otherwise) and writes all locks in one atomic save.rdm: in current note — strips every lock and clears
cached previews. The next render shows fresh previews everywhere..ipt generators.
Run this after adding or renaming generator files if the JS API's
rollUnscoped / bare-filename Use: resolution looks stale.Randomness exposes a JavaScript API for other plugins and for Templater scripts, so you can roll generators from code — for example, to populate a freshly created note from a shared generator library.
const api = app.plugins.plugins["randomness"].api;
// Roll a generator found anywhere in the vault (ignores note scope):
const r = await api.rollUnscoped("VillainName");
console.log(r.result); // -> "Mordred the Pale"
The two rolls you'll use most:
roll(name, opts?) — rolls a table in note scope (sees the
note's same-note codeblocks and Use: imports). Use it when rolling
from the context of a specific note.rollUnscoped(name, opts?) — rolls a table found anywhere in
the vault, ignoring scope. Use it for automation and note
generation, where there's no scope wired up yet — e.g. a Templater
template that builds a note from your shared generators.Both accept promptValues (override a generator's prompts by label) and
seed (deterministic rolls). rollUnscoped also accepts filePath to
disambiguate when two files define the same table name.
// Pass values into a generator's prompts, by label:
const inn = await api.rollUnscoped("TF-Inn", {
promptValues: { town: "Frostkey", shopName: "The Salty Anchor" },
});
Full reference, including every method, option, the RollResult
shape, collision handling, and recipes: see API.md.
Use: paths that don't resolve next to the calling note.HTML (rich) to enable bold/italic/list
filters as visual formatting; Plain text to keep them as plain
characters. Individual generators can override via the Formatting:
directive.rdm: calls workInline rdm: calls render in Reading view only. Obsidian's
Live Preview uses a different rendering pipeline (CodeMirror 6
extensions, not markdown post-processors), and the plugin doesn't
have a CM6 extension yet. In Live Preview the inline calls show as
plain code spans — locks in the source survive, but the 🔒/🎲
buttons don't appear.
Workflow recommendation: author your prose in Live Preview, switch to Reading view (Ctrl/Cmd-E or the read-eye icon) to roll/lock inline calls. Locks written from Reading view show up in Live Preview's underlying source immediately.
Codeblock generators (```randomness) work in both views —
those use the codeblock processor, which Live Preview does handle.
The plugin's HTML output passes through a tag whitelist before being
attached to the DOM. Allowed tags: structural (p, div, ul, ol, li, hr,
blockquote, pre, h1–h6), inline formatting (b, i, u, em, strong, s, code,
br, span, and a few others), and tables. All attributes are stripped.
Anything outside the whitelist — <script>, <iframe>, <a>, event
handlers like onclick — is dropped along with its content.
You should still only use generators you trust. The whitelist is
defence-in-depth, not a free pass to run arbitrary .ipt files from
strangers.
This plugin is MIT-licensed (see LICENSE).
The IPP3 (Inspiration Pad Pro 3) grammar and file format are the work of NBOS Software. Their Inspiration Pad ecosystem is the reason a generator written in 2008 still rolls today; this plugin aims to be a faithful, modern execution environment for that work, not a replacement for it. Use the original tools where they fit your workflow better — and consider supporting NBOS.
Generator content (.ipt files) from the wider community remains the
copyright of its original authors and is governed by whatever licenses
those authors chose. The plugin does not include or distribute generator
content; the corpus shipped in the dev repo is for testing only.
Pure-TypeScript engine and resolver, no Obsidian imports outside
src/views/. The test suite runs against in-memory file sources and
jsdom for DOM testing — no Obsidian instance required to develop.
npm install # one-time setup
npm test # run the full suite (~2s, 459 tests)
npx tsc --noEmit # strict typecheck
npm run build # bundle for distribution
npm run dev # watch mode
Architecture in three layers:
src/engine/) — pure IPP3 evaluator. AST, parsers,
expression evaluator with seedable PRNG, 21 filters, recursion guard.src/resolver/) — Use: graph traversal,
markdown-codeblock extraction, inline scope assembly. Synchronous;
async backend via asyncPrefetcher.src/views/) — the only layer that imports Obsidian.
Codeblock processor, inline processor, settings, lock/reroll state
machine, prompt UI, HTML sanitiser.See STATUS.md for the full design log including bugs found and fixed,
trade-offs accepted, and outstanding items.