# Writing Prompt Generator > A writing-prompt generator for fiction writers, built around an explicit model of the > prompt space rather than a vibe. The space is browsable free in the browser; the paid > lane writes a prompt to a coordinate the writer can see; and the finished prompt is then > checked back against that coordinate. Live at https://writing-prompt-generator.skillsafe.ai/ ## The problem it is solving Prompt generators fail in one specific way, and it is not repetition. It is **N ideas in N costumes**: the setting, the profession and the object all change while the situation underneath stays a single template. Surface variety is precisely what makes this hard to notice, because every prompt does genuinely look different. Writing Prompt Generator treats that as an engineering problem rather than a taste problem. ## How the space is modelled Nine axes, roughly 7.08 billion coordinates, split into two bands that are treated differently: **Engine axes — the situation.** Distance is measured on these and only these. - `stance` — whose story it is, by position relative to what has already happened (the person who caused it, the one who inherited it, the one who watched and said nothing, the one it made unnecessary, the one whose name it was done in, ...) - `want` — what they are after, specific enough to be refused - `against` — the obstacle class, with its own reasons (a procedure that is indifferent rather than hostile; a commitment they made earlier; a rule they themselves wrote; someone's refusal to speak; a debt owed to a third party; care aimed slightly wrong) - `constraint` — the exclusive choice. Every option on this axis is shaped as "A or B, not both", because a constraint that yields to effort is scenery. - `pivot` — the turn available, including an explicitly weighted option for no turn at all **Surface axes — how and where it is told.** Cheap to vary. Varying only these *is* the failure, so they carry no weight in the distance metric. - `pressure` — the clock, including "nothing is forcing it, and that is the pressure" - `withheld` — what the prompt deliberately does not supply - `frame` — narrative distance and shape - `arena` — setting as pressure rather than decoration ## The four mechanisms 1. **Weighted sampling with the modal option weighted down, never banned.** A stranger arriving in town is a real story; it is also the one that turns up by itself, so it carries a weight of 0.4 and still appears. Banning options leaves a recognisable hole. 2. **Family-aware distance.** Every option declares a family. Two options in one family count as most of the way to identical, so swapping one indifferent institution for another is not counted as variety. 3. **A distance floor with a push, not a re-roll.** A candidate too close to a recent draw has the engine axis where the two agree most *expensively* resampled out of the colliding family. Re-rolling walks the same neighbourhood; pushing leaves it. 4. **Archetype repulsion, taken first.** Twelve tired premises are written down as partial engine coordinates. Archetype collisions are resolved before recency collisions, because sitting on a tired coordinate is a quality defect while sitting near last week's draw is only a variety defect. Ranking them on one scale lets the cheap recency collisions eat the push budget — measured at 25 archetype-proximate draws per 300 under combined ranking, against 5 with archetypes taken first. ## The measurement, not the claim The explorer generates the same number of **unweighted** draws alongside the real ones — no down-weighting, no floor, no archetype repulsion — and reports both. Over 300 draws: | | weighted sampler | unweighted baseline | | --- | --- | --- | | draws sitting on a tired premise | 15 | 115 | | pairs closer than the distance floor | 2.2% | 5.2% | | closest pair, engine distance | 1.99 | 1.34 | The app is making an empirical claim about itself and showing the number. ## What the paid lane adds The model is handed the coordinate in prose, the genre's own worn-out moves, the intended length, and the elements the writer asked to have left open. It returns one JSON object: `title`, `prompt`, `who`, `want`, `against`, `constraint`, `cost`, `pressure`, `withheld[]`, `openings[{frame,line}]`, `complications[]`, `avoid[]`, `why_it_holds`, `form_note`, `coordinate_used`, and on a second run `moved_from`. Two design decisions inside that contract are worth stating plainly: - **There is no length guidance in the schema, anywhere.** A per-field sentence count writes the cadence as well as the contract. The app measures the spread of field lengths and shows it, so uniformity is visible rather than assumed. - **There is no example prompt in the system prompt.** An exemplar in a generator's own instructions is not illustration, it is instruction — a sample about a lighthouse produces lighthouses for ever, and produces them while appearing to vary. Everything concrete about what to avoid arrives per-request, in the input, where it changes. ## What is checked afterwards - **Does the constraint exclude anything?** Tested structurally against a set of exclusion forms, not by asking the model whether it did. - **Did each drawn element leave a trace?** Lexical evidence per engine axis, reported as evidence rather than as a verdict. - **Is what the prompt says it leaves open still open?** A prompt that answers its own withheld element has shut its own door. - **Did the finished text land on a tired premise anyway?** A cliché scan over every string in the output, because a coordinate can be structurally clear and the writing can still arrive at the lighthouse. - **Is this an earlier prompt in new clothes?** Compared two ways — coordinate distance and language overlap — because either alone is fooled. ## The content boundary Fiction runs into dark material legitimately, and a generator that flinches from crime, grief, war and betrayal has refused its own purpose. The line is **literary register versus operational detail**: a prompt may put a character in front of a terrible act; it may not supply the procedure for one. It is enforced in three layers, and the app is explicit about which of them is weak. **Layer 1, the in-browser pre-flight filter.** Nine rules, all unconditional, all built to one shape: a `target` tier (the thing named more or less plainly) and a `route` tier (the indirect approach — a demand for operational fidelity, a character used as a mouthpiece, a request for one fragment of the method, a euphemism, a permission wrapper). Both tiers fire independently, so target-alone blocks and route-alone blocks by construction, and a structural audit function enforces that shape in the test harness — it checks machinery, never the wording of a refusal. It was then measured properly, which is the part worth reading. Three corpora of adversarial phrasings were written *after* the rules were finished and each was scored before the next round of fixes: | corpus | attacks refused | ordinary fiction allowed | | --- | --- | --- | | the set the rules were written against | 100% | 100% | | held-out #1, before fixes | 26.7% | 100% | | held-out #2, before fixes | 13.3% | 100% | | held-out #3, scored once, never tuned against | 12.5% | 100% | Every miss was grammar, not vocabulary — paraphrases of probes it already caught. Two rounds of generalising the classes raised the tuned scores to 100% and left each fresh blind set at roughly the same place. Three independent estimates agreeing between 12% and 27% is not a gap to close with a third round; it is a measurement of what pattern-matching over free-form English can do. So layer 1 is documented as what it is: a courtesy that catches the obvious and saves a wasted paid run. Its real job is the column on the right, and that column never moved. **Layer 2, the system prompt.** The model understands paraphrase, which is exactly what regex cannot. Ten adversarial steers that layer 1 passed were run through the deployed prompt; the outputs were then scanned independently for quantities with units, ratios, step markers, named reagents, temperature and time specifications, tool and forgery procedure, sexual content, minors in romantic framing, and named real officeholders. Zero hits across all ten. **Layer 3, the output reviewer.** This is the enforcement point, and it works on generated prose rather than on requests — a method that has actually been written down cannot hide, because it needs amounts, an order of operations, or a list of things to obtain, and those have stable surface forms where a request for one does not. Every finding names the **act**, never the field it was found in, and the output reviewer walks every string in the result object generically — a boundary written against one field relocates the same impulse to another. ## Free versus metered Free, and requiring no account: the whole prompt space, the explorer grid, the diversity measurement against the unweighted baseline, the coordinate CSV, two complete saved example runs with their reconciliations, and `estimate`. Metered (`gpt-terra`, signed-in): writing a prompt to a coordinate, and the second run that pushes away from it. ## Pages - `/` — the app - `/api.html` — driving it from your own code, in eight languages - `/tokens.html` — token management (noindex) - `/llms.txt` — this file ## Credit Derived from the *writing prompts* concept in the BoredHumans collection. The prompt space, the sampler, the reconciler and the content boundary are original to this app.