Writing Your Own Prompt
A reusable template for coding prompts in this project — context, current state, one change, what must not change, constraints, output and how to check it — and a recap prompt for starting a fresh chat.
From given prompts to your own
In steps 1 to 5 of the game, each build page gave you a complete prompt. From step 6 on, you write it yourself, with a template, a checklist and a few hints. That is a bigger change than it sounds. A prompt for code is a small specification: whatever it leaves open, the assistant decides for you, and you only find out when you run the result (see describing the task). Writing your own prompt means making those decisions yourself: how much harder level 5 is than level 1, what happens when the browser refuses to save, which key does what.
You do not have to invent the structure every time. The prompts in steps 1 to 5 all had the same shape, and that shape is worth keeping as a template. The general idea of templates, variables and a prompt library is on Prompt Templates and Reuse; this page applies it to one coding project.
The project brief
A project brief is a short, standing description of a software project that you paste at the top of every coding prompt: what it is, where it runs, which versions and libraries it uses, and the conventions the code follows. A model knows nothing about your project except what is in the prompt (see How an AI Model Writes Code). Without a brief it fills the gaps with whatever was most common in its training data, and for a fast-moving library that is often an older version.
For the space game, a brief could look like this:
Project: RETRO JET, a small 3D space game in one CodePen 2.0 Pen with three files: index.html (a complete page; my markup is inside <body>, with <script type="module" src="script.js"></script>), style.css and script.js.
Library: Three.js 0.186.1 (r186), loaded with import * as THREE from "three"; at the top of script.js. No other libraries or imports.
Three.js choices: WebGLRenderer, renderer.setAnimationLoop for the loop, THREE.Timer for delta time (not THREE.Clock, which is deprecated).
Keyboard: event.code (not keyCode), with preventDefault for the arrow keys and Space.
Conventions: the pure game rules (no Three.js, no DOM) stay in the GAME RULES section at the top of script.js; text on screen is set with textContent, never innerHTML; no alert(); comments for a beginner.
Every line answers a question the assistant would otherwise answer for you. "Three files" and the script tag rule out answers that put everything in one HTML page. The version and the import line rule out other ways to load Three.js. The named choices rule out the older APIs that tutorials and models still suggest. The conventions keep each new answer looking like the code you already have, which makes it much easier to read (see Reading the Code You Got).
A brief goes out of date. Three.js releases a new revision every two
to three months (r183 in February 2026, r184 in April, r185 in June
or July, r186 in September), so date it and check it now and then.
With the bare "three" import, CodePen 2.0 adds the
latest version to your Pen by itself and records it in a generated
package.json (as of October 2026): look there to see
which version your Pen really uses, and put that number in the
brief. Naming the exact version you work with is the same idea as
model pinning, applied to a library.
Tools that work inside a project folder, such as coding agents, read
a standing brief from an instructions file instead, such as
AGENTS.md or CLAUDE.md (see
instructions files). In a chat assistant with copy and paste, you paste it yourself.
A template for coding prompts
A coding prompt template is a fixed outline for asking an AI assistant to change code, with labeled parts that you fill in for each change: the context, the current code, the one change you want, what must not change, the constraints, the form of the answer and how you will check it. The parts are the ones from Anatomy of a Good Prompt, in the order a code change needs them:
Context:
[THE PROJECT BRIEF]
Current state:
[WHAT WORKS NOW, IN ONE OR TWO SENTENCES]
[paste the files, each under its file name]
One change:
[ONE SENTENCE: WHAT SHOULD BE DIFFERENT AFTER THIS, WITH NUMBERS]
Must not change:
- Everything that already works.
- [WHAT MUST STAY EXACTLY AS IT IS: NAMES, KEYS, LOOK, NUMBERS]
Constraints:
- [WHAT THE CODE MAY AND MAY NOT USE]
Output:
[ALL FILES COMPLETE, OR ONLY THE CHANGED FUNCTION BETWEEN CLEAR MARKERS]
Then two lines: what you changed, and anything you were unsure about.
Check:
[WHAT I WILL SEE, TRY OR PRINT TO KNOW IT WORKED]
Also tell me how to test the change in the preview.
- Context is the project brief, pasted as it is.
- Current state is the code as it is now, working. Paste the latest version every time: an assistant that changes an older copy from earlier in the chat quietly undoes your last steps.
- One change keeps the answer small enough to check (see Prompting in Small Steps). If you need "and" twice in that sentence, it is probably two prompts.
- Must not change protects what already works; constraints limit how the new code may be written. Both are explained in the next section.
- Output says what you will paste back. In CodePen, whole files are the safest: you replace a file instead of looking for the right place to insert a fragment. The two-line summary at the end tells you what to look at first, and "anything you were unsure about" gives the assistant room to admit a guess.
- Check is the acceptance check, explained below. Asking the assistant how to test the change often turns up a case you had not thought of.
Keep the template and your brief in a text file next to your work, not only in an old chat. That is a one-person prompt library: when you improve the template, the next prompt gets the improvement too.
What must not change, and constraints
Asked to "add a high score", an assistant may also rename your functions, tidy the CSS, swap your key handling for its favorite pattern and "improve" the colors. Each of those changes may be harmless on its own, but together they make the answer impossible to review, and one of them is usually the bug you find an hour later. Two parts of the template prevent that.
Must not change lists behavior to preserve: "everything that already works", plus anything specific you care about, such as the key bindings, the colors, the 0.35-second pause you chose in step 5, or the names of the functions in the rules section.
A constraint list is the part of a coding prompt that limits how the new code may be written: which libraries, versions, browser features and coding habits are allowed or forbidden. It is a set of prompt constraints written for code. For the game:
- No new libraries and no other imports; the same Three.js version.
-
No
alert()orconfirm(); messages go in the HTML. -
Text on screen with
textContent, neverinnerHTML(see textContent and innerHTML). - New pure logic goes into the GAME RULES section, as small functions without Three.js or the DOM, so it can be tested.
- Nothing new is created inside the animation loop (meshes, geometries, materials); reuse objects from a pool.
Try it yourself
Open a new chat in an AI assistant (Claude, ChatGPT, Copilot, Gemini) and paste the first prompt. Then start another new chat and paste the second, so the first answer cannot influence it. The function is the time display from step 4 of the game.
Make this function better.
// 75.4 seconds -> "1:15"
function formatTime(seconds) {
const whole = Math.floor(seconds);
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
}
Context:
Plain JavaScript in a small browser game. No libraries.
Current state:
This function turns a number of seconds into a time for the screen. It works for times under an hour.
// 75.4 seconds -> "1:15"
function formatTime(seconds) {
const whole = Math.floor(seconds);
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
}
One change:
Times of one hour or more should show the hours too, as H:MM:SS.
Must not change:
- The function name and its one parameter.
- Times under an hour look exactly as now: 75.4 gives "1:15".
Constraints:
- No libraries, no Date and no Intl. Keep it one small function.
Output:
Only the function. Then two lines: what you changed, and anything you were unsure about.
Check:
I will test 0 → "0:00", 75.4 → "1:15", 3599 → "59:59", 3600 → "1:00:00" and 3725 → "1:02:05". Also tell me what your function returns for -5.
What to look for: what "better" became in the first answer: hours, rounding instead of cutting off, a leading zero ("01:15"), input validation, a new name, comments, or all of them; and whether 75.4 still gives "1:15". In the second answer, whether the summary matches the code and whether it says anything about -5. Then test both yourself: paste each function and the five calls into the browser console and compare the results with the list in prompt 2.
How you will know it worked
An acceptance check is a concrete test you decide on before you send a coding prompt, which tells you whether the change does what you asked: something you will see, something you will try, or values a function must return. It is the small, personal version of a success criterion and of acceptance testing, and close to test-first prompting: you decide what "right" means, not the model's explanation of its own code.
The "What you should see" lists on the build pages are acceptance
checks written for you. From step 6 on you write your own, and the
best ones contain numbers. Suppose your step 6 prompt asks for
"level 1: 8 rocks at most, one every 0.9 s, speed 20, 15% aimed;
level 10: 24, every 0.25 s, speed 44, 60% aimed, with a straight
line in between, as a function difficulty(level)". Your
acceptance check is then: print a few levels and compare. The code
below is the answer from the reference game (step 6, GAME RULES
section, copied unchanged), with a few lines added that do exactly
that:
// From the reference game, step 6 (GAME RULES section), copied unchanged.
const KILLS_PER_LEVEL = 10; // a new level every 10 rocks
const MAX_LEVEL = 10;
// Level 1 is EASY, level 10 is HARD; levels in between are mixed.
const EASY = { maxRocks: 8, spawnEvery: 0.9, speed: 20, aimChance: 0.15 };
const HARD = { maxRocks: 24, spawnEvery: 0.25, speed: 44, aimChance: 0.6 };
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function levelFor(kills) {
return Math.min(MAX_LEVEL, 1 + Math.floor(kills / KILLS_PER_LEVEL));
}
// The difficulty curve: a level -> how many rocks, how often, how fast,
// and how many aim at the ship. A straight line from EASY to HARD.
function difficulty(level) {
const t = (clamp(level, 1, MAX_LEVEL) - 1) / (MAX_LEVEL - 1); // 0 to 1
const mix = (easy, hard) => easy + (hard - easy) * t;
return {
maxRocks: Math.round(mix(EASY.maxRocks, HARD.maxRocks)),
spawnEvery: mix(EASY.spawnEvery, HARD.spawnEvery),
speed: mix(EASY.speed, HARD.speed),
aimChance: mix(EASY.aimChance, HARD.aimChance),
};
}
// The acceptance check: print the curve and compare it with the prompt.
console.log("level rocks every speed aimed");
for (const level of [1, 2, 5, 9, 10, 11]) {
const d = difficulty(level);
console.log(
String(level).padStart(5),
String(d.maxRocks).padStart(6),
`${d.spawnEvery.toFixed(2)}s`.padStart(6),
d.speed.toFixed(1).padStart(6),
`${Math.round(d.aimChance * 100)}%`.padStart(6),
);
}
console.log("35 rocks shot -> level", levelFor(35));
level rocks every speed aimed
1 8 0.90s 20.0 15%
2 10 0.83s 22.7 20%
5 15 0.61s 30.7 35%
9 22 0.32s 41.3 55%
10 24 0.25s 44.0 60%
11 24 0.25s 44.0 60%
35 rocks shot -> level 4
Level 1 and level 10 match the prompt exactly, every level in between is a little harder than the one before, and level 11 (which the game never reaches) stays at level 10 instead of running off. That took one minute and no playing. Whether level 5 is fun is a different question, which only playing answers; but if the numbers were wrong, you would know before you started playing, and you could tell the assistant exactly which numbers.
Checks that cannot print numbers are written as things to see or try: "after the first hit the SHIELD square is empty", "press RESTART, then Space: it fires instead of restarting". Write them down before you read the answer; afterward it is easy to check only what the answer says it did.
A checklist before you send
A prompt checklist is a short list of points you go through before sending a prompt, to catch what is easy to forget. It is not about knowing how to write a prompt; it is about not skipping a line when you are eager to see the next feature. For coding prompts:
- Does the prompt start with the current brief?
- Is the pasted code the latest working version, all of it, each file under its name?
- One change only, described with numbers rather than feelings?
- "Keep everything that already works", plus anything specific?
- Constraints: libraries, versions, habits?
- Which output: whole files or one marked function?
- An acceptance check you wrote down before sending?
- Nothing secret or personal in the prompt: no API keys, passwords or real customer data (see what never to paste)?
The build pages from step 6 on have a "What to mention" list for each step. That is a step-specific checklist: it names the decisions the step needs, such as how the score works or what the shield looks like, so your prompt does not leave them to the assistant. See step 6 for the first one.
Starting a fresh chat
A chat gets worse as it gets longer. The model receives the whole conversation with every new message (see In a chat, everything earlier is part of the prompt), including the three broken versions from a debugging detour and instructions you have since changed your mind about. The more there is, the harder it becomes to find and use the part that matters: context rot. Typical signs are answers that bring back code you removed, forget a constraint you gave early on, or mix up two versions of a file.
A handover prompt is the first prompt of a new chat, written to carry over everything the new conversation needs from the old one: the project brief, where the work stands, the decisions to keep, the next change and the current files. It replaces a long, noisy history with a short, clean one. Coding agents do something similar automatically when they summarize old history (see context compaction); in a chat, you do it by hand.
I am continuing a project from an earlier chat. Everything you need is below. The files are the current version, and they work.
Project brief:
[THE PROJECT BRIEF]
Where we are:
- Done and working: [THE STEPS OR FEATURES]
- Decisions to keep: [e.g. firing is driven by a burst timer, not by key repeat; the highscore is read and written inside try/catch]
- Known problems: [NONE, OR WHAT YOU NOTICED]
Next change (only this one):
[ONE CHANGE, WITH NUMBERS]
[paste index.html, style.css and script.js here]
Before you write any code, repeat in three lines what you understood. Then give me all three files complete.
"Decisions to keep" is the part a fresh chat would otherwise lose: the reasons behind the code, which are not visible in the code itself. The request to repeat what it understood is a cheap check that the handover arrived.
You can ask the old chat to write the "where we are" part for you ("Summarize what we built, the decisions we made and the open problems, in at most ten lines"). Read that summary before you use it: it is generated text like any other, and it can be wrong. When the summary and the code disagree, the code wins. A natural moment for a fresh chat is right after a step is finished and saved.