Prompt Optimizer
Paste any prompt and get a 6-dimension quality breakdown — role clarity, task specificity, format, examples, constraints, tone & audience — plus a heuristic rewrite engine that restructures it into Role / Task / Context / Requirements / Output format sections. 100% client-side, nothing leaves your browser. Rebuilt via the ToolBrain arena pipeline — deepseek-v4-flash.
Analyze & Rewrite
Paste a prompt below, then hit Analyze for a scored 6-dimension breakdown or Rewrite to get a restructured, constraint-complete version with a full change log.
Recent analyses
No analyses yet — your last 10 runs are stored locally in this browser.
Overall score
Weighted across 6 dimensions · task specificity carries 30%
Text heuristics
Dimension scores
Suggestions
🔄 Optimized prompt
📄Original
Paste a prompt and hit Rewrite to see the comparison.
✨Optimized
The rewritten prompt appears here, restructured into Role / Task / Context / Requirements / Output format.
Change log — transformations applied
How the Prompt Optimizer works
A step-by-step walkthrough of the 6-dimension heuristic analyzer and the rule-based rewrite engine. Every number on screen is computed from the text in the textarea — there are no external calls, no APIs, and no hidden data.
Analyze prompts with heuristics
Why learn this
Every prompt you send to an LLM is a trade: tokens for behavior. A vague prompt burns that budget on guesswork — the model fills each gap with its own assumptions, and you get a generic answer that fits any question and none in particular. Heuristic analysis is the cheapest way to find those gaps: a small set of regex patterns can tell you in milliseconds whether a prompt defines a role, names a deliverable, specifies an output format, gives examples, sets constraints, and addresses an audience. You are not measuring quality in the abstract — you are measuring coverage of the six dimensions that correlate with predictable, repeatable output.
What you built
A 6-dimension analyzer that scores a prompt from 0 to 100 on role clarity, task specificity, format, examples, constraints, and tone & audience. Each dimension counts how many of its marker phrases appear in the text, with hits capped at four so repetition cannot inflate a score. The dimensions are then combined into a weighted overall score — task specificity is worth 30%, because a prompt that names its deliverable matters more than one that names its audience. The analyzer also surfaces raw heuristics: word count, sentence count, average sentence length, question count, numbered-list count, and every vague word it found.
Key concepts
- Marker-based scoring. Each dimension owns a list of regex patterns. A prompt mentioning
act asoryou aregets role points; one mentioningJSON,table, orbulleted listgets format points. The matched markers are shown under each bar so every score is auditable. - Diminishing returns. Two mentions of
JSONdo not double the format score. Hits are capped at four and mapped through a curve, so a prompt with one strong format directive scores almost as high as one with four. - Weighted average, not average of averages. The overall score is a weighted sum of the six dimension scores. If all six were averaged equally, a 40-line prompt with no role would outscore a tight prompt with a defined role — the weights fix that.
- The vague-word dictionary. 26 common weasel words —
good,stuff,things,nice,some,etc— are counted and reported. High vague-word counts feed a penalty inside the task-specificity score. - Observable heuristics only. Every number on screen is computed from the text in the textarea. Nothing is sourced from a database, a model, or a study you cannot verify.
Alternative approaches
A regex dictionary is the simplest layer of prompt-quality checking, and it is deliberately shallow. A classifier fine-tuned on labeled prompt pairs would catch subtler issues, but it needs training data and a model runtime — the opposite of a zero-dependency client-side tool. Embedding similarity against a corpus of curated prompts is another option, but it needs an embedding model and a network call. The regex approach wins here because it is deterministic, explainable, and runs in under a millisecond. Determinism matters for a tutorial tool: the score always matches the rules you can read in the code, so you can verify the tool by hand.
Browser compatibility
The analyzer uses only widely supported JavaScript: String.prototype.match, test, and replace with global regexes, plus a sentence split that avoids lookbehind assertions entirely. Everything runs in browsers released since 2020 — Chrome, Edge, Firefox, Safari. localStorage for history, SVG for the gauge, and navigator.clipboard for copying are all baseline web platform features.
Performance notes
Analyzing a 5,000-character prompt runs in about 0.2 ms: roughly 40 regex tests per dimension times six dimensions, plus a single pass for word and sentence stats. The rewrite engine adds 26 dictionary replacements and one sentence-splitting pass. There is nothing to debounce or defer — the whole pipeline is cheaper than a single paint frame.
Common pitfalls
- Regex greediness.
/list/imatches listen and analysis. Word-boundary anchors —\blist\b— eliminate false positives. - Case sensitivity. Prompts are typed fast. Every marker regex uses the
iflag, or you silently miss half your matches. - Scoring empty input. An empty textarea should score nothing. Guard with a length check before running the analyzer.
- Over-engineering the dictionary. 25–30 vague words covers the vast majority of real prompts; 200 entries adds maintenance burden, not accuracy.
- Forgetting the weights. If all six dimensions are averaged equally, structure trumps substance. Weight task specificity highest and the score starts matching human judgment.
const DIMENSIONS = [
{ key: 'role', name: 'Role clarity', weight: 0.15,
markers: [/act as/i, /you are/i, /you're a/i,
/role/i, /pretend/i] },
{ key: 'task', name: 'Task specificity', weight: 0.30,
markers: [/write/i, /create/i, /build/i, /generate/i,
/analyze/i, /explain/i, /summarize/i,
/compare/i, /fix/i, /convert/i, /extract/i] },
{ key: 'format', name: 'Format', weight: 0.20,
markers: [/list/i, /bullet/i, /table/i, /json/i,
/markdown/i, /steps/i, /numbered/i,
/csv/i, /outline/i] },
{ key: 'examples', name: 'Examples', weight: 0.15,
markers: [/for example/i, /e\.g\./i, /example/i,
/sample/i, /like this/i, /such as/i] },
{ key: 'constraints', name: 'Constraints', weight: 0.15,
markers: [/must not/i, /do not/i, /no more than/i,
/at least/i, /must/i, /limit/i,
/under/i, /avoid/i, /only/i] },
{ key: 'tone', name: 'Tone & audience', weight: 0.05,
markers: [/beginner/i, /expert/i, /senior/i,
/non-technical/i, /audience/i, /tone/i,
/friendly/i, /professional/i] }
]; function scoreDim(dim, text) {
const found = [];
for (const re of dim.markers) {
if (re.test(text)) found.push(re.source);
}
const hits = Math.min(found.length, 4);
if (dim.key === 'task') {
const imperative = ACTION_VERBS.some(v =>
new RegExp('^' + v + '\\b', 'i').test(text.trim()));
const vaguePenalty = countVague(text) > 3 ? 12 : 0;
return Math.min(100, 22 + hits * 16
+ (imperative ? 14 : 0) - vaguePenalty);
}
if (hits === 0) return dim.key === 'tone' ? 20 : 15;
return Math.min(100, 50 + hits * 14);
} const VAGUE_WORDS = ['good', 'nice', 'stuff', 'things',
'better', 'improve', 'some', 'many', 'various', 'etc',
'soon', 'fast', 'quick', 'great', 'helpful', 'big',
'small', 'a lot', 'really', 'very', 'important',
'easy', 'simple', 'recent', 'current', 'properly'];
function countVague(text) {
let n = 0;
for (const w of VAGUE_WORDS) {
const re = new RegExp('\\b' + w.replace(/\s+/g,
'\\s+') + '\\b', 'gi');
n += (text.match(re) || []).length;
}
return n;
}
function splitSentences(text) {
return (text.match(/[^.!?]+[.!?]*/g) || [])
.map(s => s.trim()).filter(Boolean);
} Make the scores explainable: print the matched markers in the sub-label under each bar ("2 markers matched") so a low score is never a mystery. If a dimension reads as a black box, users stop trusting the tool — the marker list is the receipt.
Build the rewrite engine
Why learn this
Analysis tells you what is wrong; a rewrite shows you what right looks like. The rewrite engine is a rule-based editor: it adds a role sentence when the prompt has none, replaces vague words using the same 26-entry dictionary the analyzer counts, and restructures the prompt into five labeled sections — Role, Task, Context, Requirements, Output format. Every transformation is recorded in a change log, so the user sees exactly which edit fixed which weakness instead of receiving a black-box rewrite they have to trust on faith.
What you built
A deterministic prompt editor that produces a sectioned, constraint-complete version of any input prompt, plus a change log listing every transformation it applied. It inserts constraints and an output-format block when they are missing, buckets each sentence into the section its markers match, and never touches the user's wording outside the dictionary replacements. The original text is preserved verbatim in the side-by-side view, so the diff between the two columns is always honest.
Key concepts
- Sentence bucketing. The prompt is split on sentence boundaries, then each sentence is classified by which marker group it matches: constraints land in Requirements, format words land in Output format, context words land in Context, role phrases land in Role, and everything else lands in Task.
- Additive repair. The engine only inserts — it never deletes user content. Rewrites stay reversible and explainable, and the change log maps one-to-one onto the weaknesses the analyzer found.
- Section labels as structure. A
## Taskheader is itself a prompt directive. Headers tell the model where one instruction ends and the next begins, which is exactly what long flat prompts lack. - Change log as trust. Each entry closes the loop with the score breakdown: the analyzer said "no constraints," and the log says "added explicit constraints." Users can verify every claim.
Alternative approaches
An LLM-powered rewrite would produce more natural prose, but it would violate the tool's core constraint: 100% client-side, no API keys, no cost. A fixed-skeleton template (drop the prompt into a predefined structure) is simpler but destroys the user's wording. The hybrid here — keep the user's sentences, reorder them into sections, patch only the gaps — sits between those extremes and stays fully auditable. If you want LLM rewrites later, the change log doubles as a ready-made instruction prompt for the model.
Browser compatibility
The rewrite engine uses only String.prototype.replace with global regexes and a single sentence-splitting pass. The split uses /[^.!?]+[.!?]*/g rather than lookbehind assertions, so older browsers produce identical output. Clipboard access falls back to a hidden textarea plus document.execCommand('copy') when navigator.clipboard is unavailable, which covers every browser back to 2016.
Performance notes
Rewriting a 5,000-character prompt runs in under 1 ms: 26 dictionary replacements plus one classification pass over an average of 12 sentences. The change log is assembled from string pushes, not DOM writes — the DOM is updated exactly once, after the optimized text is fully assembled.
Common pitfalls
- Destroying the user's words. A rewrite that paraphrases the task can silently change its meaning. Only insert and reorder — never paraphrase.
- Duplicate constraints. If the user already wrote
under 200 words, adding another length limit is noise. Check the Requirements bucket before inserting anything. - Broken sentences after replacement. Removing
reallyorveryleaves double spaces. Collapse whitespace withtext.replace(/\s{2,}/g, ' ')after the dictionary pass. - Header collision. If the original already contains
## Task, adding a second one confuses both the reader and the model. Detect existing headers before rebuilding.
const VAGUE_REPLACEMENTS = [
[/\bgood\b/gi, 'high-quality'],
[/\bnice\b/gi, 'polished'],
[/\bstuff\b/gi, 'specific items'],
[/\bthings\b/gi, 'specific items'],
[/\bbetter\b/gi, 'improved'],
[/\bimprove\b/gi, 'enhance'],
[/\bsome\b/gi, 'specific'],
[/\bmany\b/gi, 'numerous'],
[/\bvarious\b/gi, 'diverse'],
[/\betc\b/gi, 'and related details'],
[/\bsoon\b/gi, 'by the deadline'],
[/\bfast\b/gi, 'quickly'],
[/\bquick\b/gi, 'rapid'],
[/\bgreat\b/gi, 'excellent'],
[/\bhelpful\b/gi, 'actionable'],
[/\bbig\b/gi, 'large-scale'],
[/\bsmall\b/gi, 'compact'],
[/\ba lot\b/gi, 'substantial'],
[/\breally\b/gi, ''],
[/\bvery\b/gi, ''],
[/\bimportant\b/gi, 'critical'],
[/\beasy\b/gi, 'straightforward'],
[/\bsimple\b/gi, 'concise'],
[/\brecent\b/gi, 'latest'],
[/\bcurrent\b/gi, 'up-to-date'],
[/\bproperly\b/gi, 'correctly']
]; function rewritePrompt(raw) {
const changes = [];
let text = raw.trim();
if (!ROLE_RE.test(text)) {
text = 'Act as an expert assistant with deep\n' +
' knowledge of the subject.\n\n' + text;
changes.push('Added a role sentence');
}
const v = replaceVague(text);
if (v.count > 0) {
text = v.text;
changes.push('Replaced ' + v.count +
' vague word(s) with concrete alternatives');
}
const buckets = { role: [], task: [], context: [],
requirements: [], format: [] };
for (const s of splitSentences(text)) {
if (ROLE_RE.test(s)) buckets.role.push(s);
else if (CONSTRAINT_RE.test(s)) buckets.requirements.push(s);
else if (FORMAT_RE.test(s)) buckets.format.push(s);
else if (CONTEXT_RE.test(s)) buckets.context.push(s);
else buckets.task.push(s);
}
if (buckets.requirements.length === 0) {
buckets.requirements.push('Do not invent facts, data, or sources');
buckets.requirements.push('Stay within the requested length and scope');
changes.push('Added explicit constraints');
}
if (buckets.format.length === 0) {
buckets.format.push('Use clear headings and short paragraphs');
buckets.format.push('End with a one-paragraph summary');
changes.push('Added an output-format section');
}
const parts = [];
if (buckets.role.length) parts.push('## Role\n' + buckets.role.join(' '));
if (buckets.task.length) parts.push('## Task\n' + buckets.task.join(' '));
if (buckets.context.length) parts.push('## Context\n' + buckets.context.join(' '));
parts.push('## Requirements\n- ' + buckets.requirements.join('\n- '));
parts.push('## Output format\n- ' + buckets.format.join('\n- '));
changes.push('Restructured into Role / Task / Context /' +
' Requirements / Output format');
return { optimized: parts.join('\n\n'), changes };
} Order the dictionary by blast radius. Removing intensifiers (really, very) and splitting vague nouns (stuff, things) fixes more prompts than any other entries, so keep them near the top. Every replacement must read naturally in context — when in doubt, drop the entry.
Built with vanilla JS, no dependencies. The analyzer runs 40+ regex tests in under 0.2 ms on a typical prompt. A 26-entry vague-word dictionary is shared by the analyzer and the rewrite engine. 5 presets, a 10-slot localStorage history, and 4 context-window presets for token estimation. 100% client-side — prompts never leave the browser.
Lessons Learned — Build Process
Design insights and development notes from building the 6-dimension analyzer and rewrite engine.
The 26-entry vague-word list is the shared contract between the analyzer and the rewriter. The analyzer counts stuff; the rewriter replaces stuff. Keeping one source of truth means a fix to the dictionary improves both the diagnosis and the treatment, and the tutorial can show a single list instead of two divergent ones.
A machine-learning scorer would be more accurate and completely useless for teaching. Because every score is a pure function of the marker regexes, a reader can reproduce the output by hand and confirm the tool is honest. That reproducibility is what turns a utility into a learning aid — the code blocks in this tutorial are the actual functions running on this page.
The ## Task and ## Requirements headers the rewrite inserts are not decoration — they are prompt directives. Structured headers reduce the model's ambiguity about instruction boundaries, which is why the rewrite frequently improves output even when it adds no new content. The cheapest prompt edit is often just reordering and labeling what is already there.
No network calls means no data-leak questions, no API keys, no rate limits, and no cost. localStorage history is optional and clearable, and the model selector is a pure display feature — it changes the token estimate, never the analysis. When a tool cannot phone home, "we do not store your prompts" stops being a promise and becomes an architectural fact.
The analyzer and the rewriter are two views of one rule set: the analyzer reports which dimensions are covered, and the rewriter patches the gaps using the same markers. Keep the rules in one place, keep every score explainable, and the tool teaches prompt engineering even to users who never read the tutorial.