New to OpenCode? Start with OpenCode setup, model providers and course budget, then return here for your chosen app.
Stop re-drawing the same lane boxes every time a colleague asks "how did you quantify that blot?"
A Western blot image looks convincing, but the number behind "treatment A doubles Protein X" depends on choices nobody sees: where the box was, how background was removed, which lane was the reference, and whether a band was simply too dark to measure. Blot Densitometer measures supplied lane boxes, shows every intensity profile, names its background method and refuses to put a number on a saturated band.
You will build it with OpenCode from one synthetic blot: four lanes, a target protein band on top and a loading-control band (actin) below. A loading control is a protein expected to be equal in every lane; dividing by it corrects for unequal loading. The app runs locally in your browser. It reports relative values only: no absolute protein amounts, and no claim that the bands are in the linear range of your detector. Step 15 shows how to use your own permitted blot image safely.
The real result: an app you own, a check you can repeat, and a correction you had to find yourself. The first recorded build gave the saturated lane a confident number. You will spot it in the profile and send the fix.
You will finish with: an app that gives lanes 1–3 as 1.00, 2.00 and 0.50 relative to lane 1, refuses to number the saturated lane 4, and exports its method with the results.
First watch the 1:47 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, practice blot, lanes file, four checkpoints and the 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 5 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.
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 |
|---|---|
practice-blot.png (opens in a new tab) | Step 4 · Add the practice files |
lanes.json (opens in a new tab) | Step 4 · Add the practice files |
other-blot.png (opens in a new tab) | Step 15 · Try another image, then your own permitted image |
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
blot-densitometerin Documents. - In OpenCode, click Home → Add project.
- Select your
blot-densitometerfolder. Click Select folder at bottom right. - Click New session.
Success: the new-session screen shows blot-densitometer below the message box.
If different: another folder name → return Home and add the correct folder. Open the project before choosing a model.
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 below the box. Step 5 checks that it answers.
If different: no working model → ask the instructor to check 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 this recorded interface the agent selector is hidden and Build is the default.
4. Add the practice filesLink to step
Do
- Open the extracted reference kit, then its
datafolder. - Copy
practice-blot.png(opens in a new tab) andlanes.json(opens in a new tab) into yourblot-densitometerfolder. - Double-click
practice-blot.png.
Success: a light grey image with two rows of four dark bands. The top-right band is solid black.
lanes.json is a small text file that lists, for each lane, one box around the target band and one around the actin band, in image pixels (x, y from the top-left corner, width, height). It also names the loading control and says that lane 1 is the reference lane. The boxes are supplied by the course; nothing detects bands.
If different: the file is called lanes.json.txt → rename it to lanes.json.
5. Start your OpenCode coachLink to step
Do: click once inside the message box. Paste this block. Check that blot-densitometer still appears below the box, then click the upward-arrow Send button.
Be my patient app-building coach. I am a beginner.
We will build Blot Densitometer in this folder, one step at a time.
Use one index.html with inline HTML, CSS and JavaScript,
no packages, server or network calls.
practice-blot.png and lanes.json are synthetic course data.
Explain in at most four short lines: Do, See, Success, If different.
Never claim to see my browser unless you actually inspected it.
Do not create files yet. Reply exactly:
Ready. Next: build the blot viewer.
Wait: while OpenCode answers, Send changes to Stop. Continue only after Stop disappears.
Success: the reply reads Ready. Next: build the blot viewer. No index.html exists yet.
If different: an access or quota error → return to Step 3. Files already appeared → send Stop. Remove only the files you just created, then wait.
6. Request the blot viewerLink to step
Do: paste this request into the same session and click Send once. The technical words (canvas, FileReader) are instructions for OpenCode; you do not need to know them.
Read lanes.json first. Then create only index.html: a polished,
accessible app with the heading "Blot Densitometer".
Add two labelled file inputs: "Blot image (PNG)" and "Lanes file (JSON)".
The page is opened by double-click, so read both files locally with
FileReader or object URLs; never fetch. Draw the image on a canvas and
scale an overlay with it so boxes stay aligned at any window width.
When both files are loaded and the lanes file's "image" value equals the
chosen image's file name, outline every supplied box (target and control
for each lane), label each one like "L1 target", show the target and
loading-control names from lanes.json, and show "8 boxes supplied"
(count them from the file). If the names differ, show the image with no
boxes and the message "No boxes supplied for this image"; never guess
bands. Show a clear error if a box lies outside the image. Under the
image, show the mouse position in original image pixels (x, y).
No measurements yet. Save index.html. Tell me how to open it and the
first result to check, then wait.
Wait: about three minutes in the recording. You will see tool activity such as Read, Write and Shell. Continue after Stop turns back into Send.
Success: the reply says index.html was saved and tells you to choose both files.
If different: code appears only in the chat → send Save that code as index.html in this project. Tell me the saved path. A permission question → allow writing index.html inside blot-densitometer only.
7. Open the app and check the eight boxesLink to step
Do
- In Finder/File Explorer, open
blot-densitometerand double-clickindex.html. If a text editor opens, right-click → Open With → your browser. - Under Blot image (PNG) click Choose File, select
practice-blot.png, click Open. - Under Lanes file (JSON) click Choose File, select
lanes.json, click Open. - Move the pointer over the image and watch the Mouse position line.
Success: 8 boxes supplied; each box surrounds one band; the loading control reads actin (synthetic); the mouse position changes as you move.
If different: boxes drift away from the bands → send The boxes are not on the bands at this window width. Draw image and boxes in one coordinate system scaled together. Do not move individual boxes. Wait, refresh (Ctrl+R / Command+R) and choose both files again. The browser forgets chosen files on every refresh.
Keep: copy the whole project folder beside the original and name it blot-densitometer-01-viewer. Continue in the original. Recorded version: checkpoints/05-viewer/index.html in the kit.
8. Measure every boxLink to step
Three words first. Inverted: the image stores dark as a low number (0 = black, 255 = white); the app uses 255 minus the pixel value, so a darker band gives a bigger number. Profile: a line chart of darkness down the box, one point per pixel row. Background: the darkness of the empty membrane around a band, which must be subtracted.
Do: paste this request and click Send.
The 8 boxes line up with the bands. Now measure every box.
Read the pixels from the canvas and use the red channel as the grey value.
inverted = 255 - pixel value, so a darker band gives more signal.
Profile: for each row of the box, the mean inverted value. Draw it as a
small labelled line chart for each box.
Background: the median of the inverted values in the box's top row and
bottom row together. Draw it as a dashed line on the profile.
Signal: the sum over all box pixels of (inverted - background); keep
negative differences. Add a table with one row per lane: lane, sample,
target signal, loading-control signal, target / control, and
"relative to lane N" = that lane's ratio divided by the reference lane's
ratio (referenceLane in lanes.json). Signals as whole numbers, ratios
with two decimals. No export yet. Save index.html.
Tell me which numbers to check, then wait.
Wait: about three and a half minutes in the recording.
Success: the reply names numbers to check. Notice that it presents lane 4 (5.47) as a correct result. Step 10 tests that claim.
If different: no table after refresh → confirm the reply says index.html was saved, then refresh the correct browser tab and choose both files again.
9. Check lanes 1 to 3Link to step
Do: refresh the app, choose practice-blot.png and lanes.json again, and scroll to the table.
| Lane | Sample | Target / control | Relative to lane 1 |
|---|---|---|---|
| 1 | untreated | 0.60 | 1.00 |
| 2 | treatment A | 1.19 | 2.00 |
| 3 | treatment B | 0.30 | 0.50 |
These values were computed independently from the image pixels with the same named method. The relative value is a ratio of ratios: lane 2 has 1.19 ÷ 0.60 ≈ 2 times lane 1's target-to-actin ratio.
Success: your table matches all three rows. Each of the eight profiles has a dashed background line near its base.
If different: a relative value does not match → send Lane [number] should be [value from the table above] relative to lane 1, but I see [your number]. Check that the background is the median of the top and bottom box rows only and that negative differences are kept. Change nothing else. Wait, refresh and choose both files again.
10. Find what the first build got wrongLink to step
Do: look at the profile labelled L4 target, then at table row 4.
The L4 target profile has a flat top. The middle of that band is pure black: the detector (film or camera) was saturated, meaning it could not record anything darker. The true amount of signal there is unknown, so 5.47 is not a result. A smooth, peaked profile is measurable; a flat-topped one is not. OpenCode did exactly what the request said, and told you to check 5.47 as if it were correct. The request forgot saturation, and an AI's confident reply is not a check.
Success: you can say why lane 4's number must not be reported.
If different: the profile shows no flat top → confirm you chose practice-blot.png from the kit, not another image.
11. Send the correctionLink to step
Do: paste this correction and click Send.
Lane 4 is wrong. Its target profile has a flat top: the band contains
pure black pixels (pixel value 0, inverted 255). The film or camera was
saturated there, so the true signal is unknown and 5.47 is meaningless.
For any box with at least one inverted value of 255: show
"saturated — not quantified" in that lane's row instead of its signals,
ratio and relative value, and show a visible warning that names the lane.
Mark the saturated box on the image and on its profile. If the reference
lane is saturated, show no relative values at all.
Keep lanes 1 to 3 exactly as they are. Save index.html, then wait.
Wait, then refresh and choose both files again.
Success: a Saturation warning names lane 4; row 4 reads saturated — not quantified with no numbers; lanes 1–3 still show 1.00, 2.00, 0.50.
If different: lane 1 to 3 values changed → send Lanes 1 to 3 changed after the saturation fix. They must stay 1.00, 2.00 and 0.50. Restore them and keep the lane 4 flag.
Keep: copy the project as blot-densitometer-02-saturation. Recorded version: checkpoints/07-saturation/index.html.
12. Add the export and the honesty statementLink to step
Do: paste this request and click Send. A CSV is a plain text table that any spreadsheet opens.
Lanes 1 to 3 still show 1.00, 2.00 and 0.50; lane 4 is flagged.
Add an "Export CSV" button, disabled until results exist. One row per
lane with the columns: lane,sample,target,loading_control,target_signal,
control_signal,target_to_control,relative_to_reference,saturated,
background_method. background_method must be exactly
"median of top and bottom box rows (inverted = 255 - pixel)".
Saturated lanes keep empty number cells and saturated=yes.
Quote fields correctly. File name: blot-densitometer-results.csv.
Above the table show this statement: "Relative values only. No absolute
protein amounts. Linear range not checked: that needs a dilution series."
Preserve everything else. Save index.html. Tell me what to check, then wait.
Wait, then refresh and choose both files again.
Success: the statement appears above the table and Export CSV is enabled.
If different: the statement is missing or reworded → send Show exactly this sentence above the table: "Relative values only. No absolute protein amounts. Linear range not checked: that needs a dilution series."
13. Check the exported CSVLink to step
Do: click Export CSV. Open blot-densitometer-results.csv from Downloads in a text editor or spreadsheet.
Success: five lines: the header and one row per lane. Every row names actin (synthetic) as loading control and the background method. Lane 4 has empty number cells and saturated = yes. Shortened here to four of the ten columns (the real file also puts every value in quotation marks; a spreadsheet hides them):
lane,loading_control,relative_to_reference,saturated
1,actin (synthetic),1.00,no
2,actin (synthetic),2.00,no
3,actin (synthetic),0.50,no
4,actin (synthetic),,yes
If different: the method column is missing or lane 4 has numbers → send The CSV must name the background method in every row and leave lane 4's number cells empty with saturated=yes. Change only the export.
Keep: copy the project as blot-densitometer-03-export. Recorded version: checkpoints/08-export/index.html.
14. Make it yoursLink to step
Do: send this to OpenCode:
Change only the visible heading to "My Blot Densitometer".
Keep the browser title consistent.
Do not change calculations, profiles, table or export.
Save index.html and tell me which lines changed.
Refresh the app and choose both files again.
Success: your heading appears. Lane 2 still reads 2.00; lane 4 still reads saturated — not quantified.
If different: anything else changed → ask OpenCode to undo that part, or restore index.html from your -03-export copy.
15. Try another image, then your own permitted imageLink to step
Privacy rule: never paste or attach unpublished or personal data to an AI model without permission. The app itself reads images inside your browser and sends nothing. Keep your own images and lanes files outside the project folder, so OpenCode never reads them. Use only images you are allowed to use.
Do (another image): in the app, choose other-blot.png (opens in a new tab) from the kit's data folder under Blot image, and keep the practice lanes.json.
The recorded build correctly said No boxes supplied for this image, but the panel beside it still said 8 boxes supplied. That contradiction was corrected with:
I chose other-blot.png with the practice lanes.json. The status says
"No boxes supplied for this image", which is right, but the panel beside
it still says "8 boxes supplied". For an image that does not match the
lanes file, show "0 boxes supplied", no profiles and no table, and keep
Export CSV disabled. Two small fixes as well: the "L4 target — SATURATED"
label covers the L3 target label, and the footer still says
"no export yet". Keep every number and the practice result unchanged.
Save index.html, then wait.
Success: the image appears with No boxes supplied for this image and 0 boxes supplied; no profiles, no table, Export CSV disabled. The app never invents bands.
Do (your image):
- Export your permitted blot as a greyscale PNG, without contrast adjustment (adjusting contrast changes the pixel values you measure).
- Copy
lanes.jsonto a folder outside the project and rename the copymy-lanes.json. Open it in Notepad (Windows) or TextEdit → Format → Make Plain Text (Mac). - Change
"image"to your PNG's exact file name, and"target"and"loadingControl"to your proteins. - In the app, choose your PNG. Hover over the top-left corner of each band's intended box and note x and y from Mouse position. Enter x, y, w (width) and h (height) for each box. Leave empty membrane at the top and bottom of every box: those rows are the background.
- Choose
my-lanes.jsonunder Lanes file.
Success: your boxes sit on your bands, each profile is a single peak above its dashed background line, and flat-topped bands are flagged, not numbered.
If different: a box error names a lane → that box extends beyond the image; correct its numbers. A band is flagged saturated → re-image with a shorter exposure; do not report that lane. Differences between lanes are only meaningful within the linear range, which this app does not check.
16. Leave a README and continue in a fresh sessionLink to step
Do: send this to OpenCode:
Read index.html, then create README.md with: exact opening steps
(double-click index.html, choose practice-blot.png and lanes.json); the
expected practice results (relative to lane 1: 1.00, 2.00, 0.50; lane 4
saturated — not quantified); the named background method; what the app
does not do (no band detection, no absolute amounts, linear range not
checked); the lanes.json format; and one next task: let me drag a box to
adjust it. Do not change index.html. Tell me which file you created,
then wait.
After Stop disappears, click the + (New session) button at the top, next to the session tab. Confirm blot-densitometer and your model are shown, then send:
Read README.md and index.html without editing.
In three bullets tell me the app's purpose, how to open it,
and the recorded next task.
Success: the new session names the app's purpose, how to open it, and "drag a box to adjust it" from the files alone.
If different: it answers about another folder → click Home, choose blot-densitometer, click New session and ask again. Wrong README content → ask OpenCode to correct README from index.html only.
17. Make your next improvementLink to step
Do: save a working checkpoint copy first. Then write one request, for example the recorded next task:
Add [one behaviour] so [my user] can [purpose].
Keep the practice result: 1.00, 2.00, 0.50 and lane 4 saturated.
I will know it works when [visible result].
Tell me which part will change, implement only this, then wait.
Give a colleague index.html, README.md and the two practice files. Ask them to follow the README without your help.
Success: your feature works, the practice result is unchanged, and your colleague gets 2.00 for lane 2 and a saturation flag on lane 4.
If stuck: paste into your coach session: I am on step [number]. I did: [action]. I expected: [result from the manual]. I see: [exact text]. Give me one next action, then wait.
The method behind these steps
Structure it: one question per lane, answered only relative to a named reference and loading control. Understand it: inverted pixels, a named background, and why a flat-topped profile has no number. Choose tools: supplied boxes and one local HTML file instead of hidden detection. Build it: one request at a time, each checked against independently computed values, including the correction you found. See T01-L01 · From Idea to Working Result: The Four-Quadrant Method.










