New to OpenCode? Start with OpenCode setup, model providers and course budget, then return here for your chosen app.
Stop recalculating ΔΔCt by hand in a spreadsheet
Every qPCR run ends the same way: copy Ct values out of the instrument export, match each well to your plate plan, average the replicates, subtract, subtract again, raise 2 to a power. One misaligned row and the fold change is wrong without any warning. qPCR Fold Change takes the instrument export and your plate layout and traces one gene from Ct to fold change, showing every intermediate number.
You will build it with OpenCode, one request at a time, and check each number yourself. The app runs locally in your browser: one file, no installation, no upload. The practice plate is synthetic: IL6 (target gene) and GAPDH (reference gene) in three samples, three wells each, with one failed well. Your own permitted data comes in at Step 16.
The real result: a calculator you own, understand and can extend. You will practise the whole cycle, including two real corrections the recorded build needed.
You will finish with: a local calculator that turns an instrument export and a plate layout into fold changes you can recompute by hand (1.00, 8.00, 0.50), with the failed well visibly excluded and the method exported.
First watch the 1:53 result tour. It shows where you are going. The numbered steps below are the lesson: what to click in OpenCode, what to paste, and what must appear before you continue.
Silent, with on-screen explanations. OpenCode scenes are captures from the recorded build; app scenes operate the actual OpenCode-generated app. Captions and highlights were added afterwards. Read the transcript (opens in a new tab) · Download the movie (opens in a new tab).
Use: keep this manual beside OpenCode and your browser. Complete each Success check before continuing. Click any screenshot to enlarge it.
Files: reference kit: working app, four checkpoints, practice CSVs and exact prompts (opens in a new tab). Extract the ZIP first: Windows right-click → Extract All; Mac double-click. Keep it beside your project.
Your personal coach: Step 4 turns your OpenCode session into a one-step-at-a-time guide. For help later, paste the complete coach brief (opens in a new tab) into your project session and tell it your step number.
Three words you will meet. Ct (cycle threshold): the PCR cycle at which a well's signal crosses the detection line. A lower Ct means more starting material. Reference gene: a gene expected to stay constant (here GAPDH); it corrects for how much material went into each well. Calibrator: the sample every other sample is compared with (here Control).
Files for this journey
Download everything at once as the reference kit (ZIP) (opens in a new tab), or single files as you reach the step that needs them. Save each file into your project folder under exactly this name. If a file opens in the browser instead of downloading, press Ctrl+S (Mac: Command+S) to save it.
| File | First used in |
|---|---|
plate.csv (opens in a new tab) | Step 2 · Open your project |
layout.csv (opens in a new tab) | Step 2 · Open your project |
broken-layout.csv (opens in a new tab) | Step 2 · Open your project |
plate-text-ct.csv (opens in a new tab) | Step 2 · Open your project |
The kit also contains the finished app, the recorded checkpoints (checkpoints/…) and the exact prompts.
1. Get OpenCode readyLink to step
Do
- Open an ordinary website. Complete your university Wi-Fi sign-in if prompted.
- Install OpenCode Desktop: follow Step 1 of Set up OpenCode in the L1 book Start With OpenCode.
- Open OpenCode. For English labels, use Settings → Preferences → Language → English. In German: Einstellungen → Präferenzen → Sprache → English.
Success: OpenCode opens. Continue to the project; choose the model in Step 3.
If different: installation or Wi-Fi failure → use the seminar recovery card.
2. Open your projectLink to step
Do
- In Finder or File Explorer, create a folder named
qpcr-calculatorin Documents. - Copy the
datafolder from the extracted reference kit intoqpcr-calculator. It holds four small CSV files (plain-text tables):plate.csv(opens in a new tab),layout.csv(opens in a new tab),broken-layout.csv(opens in a new tab) andplate-text-ct.csv(opens in a new tab). - In OpenCode, click Home → Add project.
- Select your
qpcr-calculatorfolder. Click Select folder. - Click New session.
Success: the new-session screen shows qpcr-calculator below the message box.
If different: another folder name → return Home and add the correct folder.
3. Select a working modelLink to step
Do: click the model name below the message box. Select the model assigned for the seminar. Wait until its name appears below the box.
Success: a model name is displayed. The next step checks that it answers.
If different: no working model or access → ask the instructor to confirm the seminar connection. This recording used Space Bunny Free; free availability is not guaranteed.
Agent control: if Plan is visible, select Build before requesting edits. In the recorded interface the agent selector is hidden and Build is the default.
4. Start your OpenCode coachLink to step
Do: copy this block, paste it into OpenCode's message box and click the upward Send arrow.
Be my patient app-building coach. I am a beginner.
We will build qPCR Fold Change in this folder, one step at a time.
Use one index.html with inline HTML, CSS and JavaScript,
no packages, server or network calls.
The data folder holds synthetic course data: plate.csv and layout.csv.
Explain in at most four short lines: Do, See, Success, If different.
Wait for my result before continuing.
Never claim to see my browser unless you actually inspected it.
Do not create files yet. Reply:
"Ready. Your project is qpcr-calculator. Next: load the plate."
Wait: while OpenCode works, Send turns into Stop. Continue only when Stop has disappeared.
Success: OpenCode replies Ready. Your project is qpcr-calculator. No file is created yet.
If different: an access or quota error → return to Step 3. Do not reinstall OpenCode to fix a model quota.
5. Load the plate: your first screenLink to step
Before any calculation, the app must join two files correctly. plate.csv (opens in a new tab) is what the instrument exports: each well and its Ct. layout.csv (opens in a new tab) is your plate plan: which sample and gene is in each well. The app matches them by well name.
Do: paste this into the same session and click Send. Wait for Stop to disappear (about two minutes in the recording).
Create only index.html in this project. Build the first qPCR Fold Change
screen with inline HTML, CSS and JavaScript: no packages, server or network.
Clean light design, dark teal heading, system fonts, labelled controls.
Add a "Load example" button and two file inputs labelled
"Plate export (plate.csv)" and "Plate layout (layout.csv)".
Read data/plate.csv and data/layout.csv now and embed their exact rows
as the example, because a double-clicked page cannot read files by itself.
plate.csv columns: well,ct. layout.csv columns: well,sample,gene.
Join them by well into one table: well, sample, gene, Ct, status.
A Ct of "Undetermined" is excluded, never 0: status "Excluded: Undetermined".
When the example loads, show "18 wells · 1 excluded".
Every plate well needs exactly one layout row, and every layout row a plate well.
Otherwise show a visible error naming each unmatched well, and no table.
Initially show "No plate yet. Click Load example."
No calculations or charts yet. Save index.html, tell me how to open it, then wait.
Success: OpenCode says index.html was saved and tells you to open it.
If different: code appears only in the chat → send "Save that code as index.html in this project. Tell me the saved path." If a permission request appears, allow the file edit only when it names your project.
6. Open the app and check the wellsLink to step
Do
- In Finder or File Explorer, open
qpcr-calculatorand double-clickindex.html. If a text editor opens instead, right-click → Open With → your browser. - Click Load example. Scroll to Joined wells.
Success: the message reads 18 wells · 1 excluded. Row C3 (Treated-2, IL6) shows Undetermined and Excluded: Undetermined. All other rows say Included.
If different: C3 shows 0 or Included → send "C3 is Undetermined. It must be excluded with status Excluded: Undetermined, never counted as 0. Fix only that." Refresh the browser (Ctrl+R, Mac Command+R) and click Load example again.
7. Break the layout on purpose, then correct the messageLink to step
A plate plan with one typo is a classic source of wrong results. broken-layout.csv (opens in a new tab) lists well B7 instead of B5. The app must refuse to calculate and say which wells do not match.
Do: click Plate export (plate.csv) and choose data/plate.csv (opens in a new tab). Then click Plate layout (layout.csv) and choose data/broken-layout.csv (opens in a new tab).
The first recorded build did catch the problem, but its message contained the word undefined, a sign of a programming mistake:
Do: report what you saw, not "it is broken". Send:
I chose data/plate.csv and data/broken-layout.csv. The error reads:
"Unmatched wells: B5 (undefined layout rows; exactly one required)".
The word "undefined" is a bug. Write one plain sentence per problem, e.g.
"Well B5 is in plate.csv but has no row in layout.csv." and
"Well B7 is in layout.csv but not in plate.csv."
Keep the table hidden while there is an error.
When I then choose the correct layout.csv, the error must disappear and all 18 wells return.
Change only this. Save index.html; tell me what to check, then wait.
After Stop disappears, refresh the browser. Choose plate.csv (opens in a new tab) and broken-layout.csv (opens in a new tab) again.
Success: two plain sentences name B5 and B7. No table is shown. Now choose data/layout.csv (opens in a new tab) in the layout input: 18 wells · 1 excluded returns.
If different: your build already showed plain sentences → skip the correction. The table stays visible beside the error → send "While the layout error is shown, hide the wells table. Change only that."
8. Keep your first working versionLink to step
Do: copy the whole qpcr-calculator folder beside the original. Name the copy qpcr-calculator-01-working. Continue working in the original.
Success: there are two folders. Both contain index.html and data.
If stuck: the kit's checkpoints/06-layout-fix/index.html is the recorded working version. Copy it into a new recovery folder; keep your own work.
9. Add the first calculation: ΔCtLink to step
ΔCt ("delta Ct") corrects each sample for how much material went into its wells: mean Ct of the target gene minus mean Ct of the reference gene. The standard deviation (SD) shows how far the replicate wells scatter; the n−1 version is the usual one for a few replicates.
Do: send:
The broken layout now names B5 and B7, and the correct files show 18 wells.
Now add the first calculation. Add a "Reference gene" dropdown filled from
the layout genes; default GAPDH. Every other gene is a target gene.
For each sample and gene, use only included wells: n, mean Ct and
sample standard deviation (SD, n−1 denominator).
Add a results table, one row per target gene and sample: sample, target,
n target, mean Ct target ± SD, n reference, mean Ct reference ± SD,
ΔCt = mean target − mean reference, SD of ΔCt = √(SD target² + SD reference²).
Compute from unrounded values; display two decimals.
Fewer than 2 wells: SD shows "—". No included wells: show "—" and say why.
For Control expect IL6 26.00 ± 0.10, GAPDH 18.00 ± 0.05, ΔCt 8.00 ± 0.11.
Treated-2 uses only 2 IL6 wells: 27.20 ± 0.14, ΔCt 9.00 ± 0.15.
No fold change or chart yet. Keep the well table and all checks.
Save index.html; tell me the next click and expected result, then wait.
Wait for Stop to disappear. Refresh the browser and click Load example.
Success: a Reference gene dropdown shows GAPDH and a Calculations table appears below the wells.
If different: no new table → check that OpenCode saved index.html, then refresh the correct browser tab.
10. Check ΔCt by handLink to step
Do: compare the Calculations table with this one. Check at least the Control row with a calculator: IL6 wells 26.10, 25.90, 26.00 → mean 26.00; GAPDH 18.00, 17.95, 18.05 → mean 18.00; ΔCt = 26.00 − 18.00 = 8.00; SD of ΔCt = √(0.10² + 0.05²) = 0.11.
| Sample | n IL6 | IL6 mean ± SD | n GAPDH | GAPDH mean ± SD | ΔCt | SD of ΔCt |
|---|---|---|---|---|---|---|
| Control | 3 | 26.00 ± 0.10 | 3 | 18.00 ± 0.05 | 8.00 | 0.11 |
| Treated-1 | 3 | 23.00 ± 0.15 | 3 | 18.00 ± 0.10 | 5.00 | 0.18 |
| Treated-2 | 2 | 27.20 ± 0.14 | 3 | 18.20 ± 0.05 | 9.00 | 0.15 |
Success: every value matches. Treated-2 IL6 uses 2 wells (C1, C2), not 3: the Undetermined well is left out, not counted as zero.
If Treated-2 shows 18.13 or n = 3: the Undetermined well became 0 → send "Treated-2 IL6 must use only C1 and C2 (mean 27.20). C3 is Undetermined and must be excluded, not treated as 0. Fix only that."
If Control shows SD 0.08: the app used the n denominator → send "Use the sample SD with n−1. Control IL6 must show 26.00 ± 0.10."
Keep: copy your project as qpcr-calculator-02-working. Recorded recovery file: checkpoints/07-delta-ct/index.html in the kit.
11. Add ΔΔCt and fold changeLink to step
ΔΔCt compares each sample with the calibrator: ΔCt of the sample minus ΔCt of Control. Fold change = 2^(−ΔΔCt) (the Livak method) assumes each PCR cycle exactly doubles the product, i.e. about 100% efficiency. The range carries the replicate SD through the same formula. It is not a confidence interval.
Do: send:
ΔCt is 8.00 for Control, 5.00 for Treated-1 and 9.00 for Treated-2.
Add a "Calibrator sample" dropdown filled from the samples; default Control.
For each result row add ΔΔCt = ΔCt − ΔCt of the calibrator (same target)
and fold change = 2^(−ΔΔCt). This is the Livak (2^−ΔΔCt) method.
Treat the calibrator's mean ΔCt as fixed, so SD of ΔΔCt = SD of ΔCt.
Add a fold-change range column: 2^(−(ΔΔCt + SD)) to 2^(−(ΔΔCt − SD)).
Two decimals. Expect Control 1.00 (0.93–1.08), Treated-1 8.00 (7.06–9.06),
Treated-2 0.50 (0.45–0.55).
Show this sentence near the results, always visible:
"Assumes ~100% amplification efficiency. No statistical test."
Do not show p-values, stars or the word "significant".
Keep everything else. Save index.html; tell me what to check, then wait.
Wait, refresh, click Load example.
Success:
| Sample | ΔΔCt | Fold change | Range |
|---|---|---|---|
| Control | 0.00 | 1.00 | 0.93–1.08 |
| Treated-1 | -3.00 | 8.00 | 7.06–9.06 |
| Treated-2 | 1.00 | 0.50 | 0.45–0.55 |
Check Treated-1 by hand: ΔΔCt = 5.00 − 8.00 = −3.00, and 2^3 = 8. The sentence Assumes ~100% amplification efficiency. No statistical test. is visible.
In the recorded build the right edge of the table was cut off. Scroll sideways inside the table to read the range; Step 12 fixes this.
Then: choose Treated-1 as Calibrator sample. Treated-1 now shows ΔΔCt 0.00 and fold change 1.00; Control shows ΔΔCt 3.00. Switch back to Control.
If different: a p-value or "significant" appears → send "Remove every p-value and significance word. This app runs no statistical test."
12. Add the chart, then make the table fitLink to step
Do: send:
Fold changes match: Control 1.00, Treated-1 8.00, Treated-2 0.50.
Add a labelled bar chart of fold change per sample, using inline SVG and
the SAME result rows as the table. Draw each range as a thin vertical
whisker from its low to its high value. Label each bar with its value.
The axis starts at 0. Add a dashed horizontal line at 1 labelled "no change".
Put the sentence "Assumes ~100% amplification efficiency. No statistical test."
directly under the chart as well. Keep the table as the text alternative.
The chart must update when I change the reference gene or calibrator.
Keep everything else. Save index.html; tell me what to check, then wait.
Refresh, click Load example. The chart appears, but in the recording the results table was still cut off at the right. Send this correction:
The chart works. But at a 1440-pixel-wide browser window the results table
is cut off at the right: the fold-change range column is hidden and
"Fold change" is clipped. Make the whole results table readable without
sideways scrolling on a laptop screen: use the width of the page,
shorter headers if needed, and keep sample names on one line.
On a phone, sideways scrolling inside the table is fine.
Change only the layout. Save index.html; tell me what to check, then wait.
Refresh again and click Load example.
Success: three bars labelled 1.00, 8.00 and 0.50, each with a thin whisker; a dashed no change line at 1; the assumption sentence under the chart. The whole table, including fold-change range, is visible without sideways scrolling.
If different: your table already fitted → skip the correction. The chart ignores the calibrator → send "When I choose Treated-1 as calibrator the table changes but the chart does not. Draw the chart from the same result rows."
Keep: copy the project as qpcr-calculator-03-working. Recorded recovery file: checkpoints/10-table-fit/index.html.
13. Export the results with the methodLink to step
A number without its method is hard to reuse. The export writes the method, reference gene, calibrator and efficiency assumption into every row.
Do: send:
The whole results table now fits.
Add an "Export results CSV" button, disabled until results exist.
One row per result row, with these columns in this order:
method,reference_gene,calibrator,efficiency_assumption,target,sample,
n_target,n_reference,mean_ct_target,sd_target,mean_ct_reference,sd_reference,
delta_ct,sd_delta_ct,delta_delta_ct,fold_change,fold_change_low,fold_change_high,
excluded_wells
method is "Livak 2^-ddCt"; efficiency_assumption is "~100% (not measured)".
excluded_wells lists that row's excluded wells, e.g. "C3 (Undetermined)".
Numbers with 4 decimals; an empty cell where the screen shows "—".
Quote fields correctly. Filename: qpcr-fold-change-<calibrator>.csv.
Keep everything else. Save index.html; tell me what to check, then wait.
Refresh, click Load example, then Export results CSV. Open qpcr-fold-change-Control.csv from Downloads in a text editor or spreadsheet.
Success: a header and three rows. Each row starts Livak 2^-ddCt,GAPDH,Control,~100% (not measured),IL6. Treated-1 has fold_change 8.0000, low 7.0603, high 9.0648. Treated-2 has n_target 2 and excluded_wells C3 (Undetermined).
If different: a spreadsheet shows dates or odd numbers → open the file in a text editor instead; the file is what counts. Missing method columns → send "Every exported row must start with method, reference_gene, calibrator and efficiency_assumption."
Keep: recorded recovery file checkpoints/11-export/index.html.
14. Make it yoursLink to step
Do: send:
The export has three rows and the method columns.
Change only the visible heading to "My qPCR Calculator".
Keep the browser title consistent.
Do not change data, checks, calculations, chart or export.
Save index.html and tell me which lines changed.
Refresh and click Load example.
Success: the heading reads My qPCR Calculator. Treated-1 still shows fold change 8.00. OpenCode names two changed lines: the browser title and the heading.
If different: anything else changed → ask OpenCode to undo that part, or restore index.html from your latest checkpoint.
15. Leave a README and continue in a fresh sessionLink to step
Do: send:
Read index.html, then create README.md with exact instructions for
opening and using this app: the two CSV files and their columns, that every
plate well needs one layout row, that Undetermined wells are excluded,
where data is processed, the method (Livak 2^-ddCt, sample SD, SD of ΔCt
carried into the range), the stated limits (assumes ~100% efficiency,
no statistical test), and the known checks: ΔCt 8.00/5.00/9.00 and fold
change 1.00/8.00/0.50 with Control as calibrator.
Record this next task: Pfaffl method with efficiencies from a supplied
standard curve. Do not implement it. Do not change index.html.
Tell me which file you created, then wait.
Then click New session (the + beside the session tab). Confirm qpcr-calculator is still the project. If the button says New project, click it, select your original qpcr-calculator folder and click Select folder. Send:
Read README.md and index.html without editing.
Tell me the app's purpose, how to open it and the recorded next task.
Keep the answer to three bullets.
Success: the new session names the app's purpose, double-clicking index.html, and the Pfaffl next task, all from your files.
If different: empty project → check the folder. Wrong facts → correct README.md before continuing; it is your memory between sessions.
16. Test a bad value, then try your own permitted dataLink to step
Real exports contain surprises: n/a, No Ct, a blank. Only Undetermined is excluded on purpose; anything else must stop the calculation. Test that before trusting your own file.
Do: in the app choose data/plate-text-ct.csv (opens in a new tab) as plate export and data/layout.csv (opens in a new tab) as layout. Well B2 contains n/a.
The recorded build listed B2 as Included and showed "—" for all Treated-1 results, with no explanation. Continue in your fresh session and send:
Before I use my own data I tested data/plate-text-ct.csv with layout.csv.
Well B2 has the Ct "n/a". The app lists B2 as "Included" and Treated-1
shows "—" everywhere. A Ct must be a number or exactly "Undetermined".
For anything else show a visible error naming the well and its value, e.g.
Well B2 has Ct "n/a". Use a number or Undetermined.
Hide the tables and chart and disable export while the error is shown.
Choosing the correct plate.csv again must bring all 18 wells back.
Change only this. Save index.html; tell me what to check, then wait.
Refresh and choose the two files again.
Success: the error names B2 and n/a; no results are shown. Choosing data/plate.csv (opens in a new tab) again brings back 18 wells · 1 excluded and fold change 8.00 for Treated-1.
Now your own data
- Use only data you are allowed to use. Never paste unpublished or personal data into a model without permission. The app itself runs in your browser and sends nothing; OpenCode sees only what you paste into it or ask it to read.
- Make a copy of one plate's export. In a spreadsheet, keep two columns named
well,ct. Make a matchinglayout.csv(opens in a new tab) withwell,sample,gene. Save both as CSV UTF-8. - Choose both files in the app. Pick your reference gene and calibrator.
- Calculate one sample's ΔCt by hand, as in Step 10, and compare.
Success: your hand calculation matches the app, and every well is either included or excluded for a reason you can see.
If different: your instrument uses other column names or another word than Undetermined → do not rename values by guesswork. Tell OpenCode the real names and ask for that one adaptation, using a made-up example row rather than your real data. Only use this app's fold change if your primer efficiencies are close to 100%; otherwise see Step 18.
17. Let a colleague use itLink to step
Do: give a colleague index.html, README.md and the data folder. Ask them to follow README.md, load the example, and tell you the fold change of Treated-2. Let them click without your help.
Success: they report 0.50 (range 0.45–0.55) and can say what "Assumes ~100% amplification efficiency" means for that number.
If they stop: add the missing instruction to README.md. Repeat from that instruction.
18. Make your next improvementLink to step
Do: save a working checkpoint. The recorded next task is the Pfaffl method: it uses each gene's measured amplification efficiency instead of assuming 100%. Efficiencies must come from a standard curve you supply (a dilution series), never from a guess. Write your request with this pattern:
Add [one behaviour] so [my user] can [purpose].
Keep [working features].
I will know it works when [visible result].
Tell me which part will change, implement only this improvement,
then wait for me to try it.
For Pfaffl: with both efficiencies set to 100% (a factor of 2.0), the result must equal the Livak result, so Treated-1 still gives 8.00.
Success: the new behaviour works; with Control as calibrator the example still gives 1.00 / 8.00 / 0.50. Record the next task in README.md.
If stuck: paste this into your coach chat:
I am on step [number].
I did: [exact action].
I expected: [result from the manual].
I see: [actual text/result, or describe a screenshot].
Give me one next action. Wait for my result.
What this app does not do: it runs no statistical test and gives no p-values; it does not measure efficiency; it makes no MIQE (qPCR reporting guideline) compliance claim; it is a teaching calculator, not a validated analysis tool.
The method behind these steps
Structure it: two input files, one well per row, with a visible rule for every mismatch. Understand it: compute ΔCt, ΔΔCt and fold change by hand before trusting the screen. Choose tools: one offline HTML file, OpenCode as the builder, your calculator as the judge. Build it: one request, one check, one checkpoint at a time, with corrections phrased as observations. Read T01-L01 · From Idea to Working Result: The Four-Quadrant Method.












