What this page is
A live, interactive preview of the PaperScorer correction embed — the same widget you'd drop into your own app to let users fix invalid student IDs and rebubble mis-read answers on scanned sheets. The demo uses canned data served from this site; no real sheet is scored.
Live embed
The two widgets below are both auto-mounted via the
data-ps-correction-embed drop-in pattern —
no JavaScript glue is running on this page beyond
correction.js itself. They illustrate the
two correction flows the embed handles: fixing a
misread student ID, and fixing misread bubble answers.
In production, which view a partner shows depends on
what the engine couldn't resolve about the scanned
sheet; the embed picks the right one automatically
based on the sheet data.
Demo 1 — Student ID correction
The engine couldn't bubble out a student identifier from this sheet. The embed hides the question grid (bubble data isn't reliable without a resolved student) and prompts for the corrected ID. Type a value and click Submit student ID.
Demo 2 — Answer bubble correction
The student ID is already resolved (pre-filled per-student sheet), but the engine flagged question 2 for multiple marked bubbles. The embed hides the student-ID input and renders the bubble grid for editing — toggle a different answer on Q2 and click Submit answer corrections.
Demo 3 — All editable question types
One question per editable type (1–8) for debugging side-by-side. Every type renders the same flat bubble-grid UI today; the semantic difference is single-select (types 1, 4, 8) vs. multi-select (types 2, 3, 5, 6, 7). Toggle a few bubbles in each section to see how the embed handles the different interaction rules, then submit to inspect the payload shape (the demo backend echoes it back).
Integrate it on your own page
The script URL — two flavors
The embed bundle is served from two URLs, and which one you pick is a caching decision:
-
/embed/dist/correction.{hash}.js— recommended.{hash}is the first 8 hex chars of the bundle file's md5. Any value works as the hash suffix — our nginx rewrites every/embed/dist/correction.*.jsURL to the same physical file and serves it withCache-Control: public, immutable(1-year cache). When we ship a new bundle, the md5 changes, the URL you render changes, browsers cache-miss and refetch. Zero stale-cache pain, aggressive caching for everyone else. This page uses that pattern — view source: the script tag at the bottom is/embed/dist/correction.74bc8bad.js. -
/embed/correction.js— the simpler fallback. Same bundle bytes, no version segment; served withCache-Control: must-revalidate, max-age=3600so updates propagate within an hour. Use this when you can't render HTML server-side or you don't want to deal with hashing.
Computing the md5 in your template (any backend language works the same — fetch our bundle once at build/deploy time, hash the bytes, render the URL):
// PHP
$bundle = file_get_contents('https://learnosity.paperscorer.com/embed/correction.js');
$hash = substr(md5($bundle), 0, 8);
$src = "https://learnosity.paperscorer.com/embed/dist/correction.{$hash}.js";
// Node
const crypto = require('crypto');
const bundle = await fetch('https://learnosity.paperscorer.com/embed/correction.js')
.then(r => r.arrayBuffer());
const hash = crypto.createHash('md5').update(Buffer.from(bundle))
.digest('hex').slice(0, 8);
const src = `https://learnosity.paperscorer.com/embed/dist/correction.${hash}.js`;
Cache the resulting URL on your side (memoize at process start, or recompute on deploy) — you don't need to re-hash on every page render.
Option A — Auto-mount (HTML drop-in)
The simplest path: mark a container with
data-ps-correction-embed and point it at
two URLs on your backend. The embed scans the
DOM on DOMContentLoaded, fetches the
sheet, and POSTs corrections back when the user
submits. Your backend brokers the call to
/api/get-correction-sheet and
/api/submit-correction-sheet with header
auth — your Consumer-Key never reaches
the browser. The {{ hash }}
placeholder below is the value from the snippet above.
<div data-ps-correction-embed
data-fetch-url="/your-backend/correction-data?task_id=619&form_id=F-100"
data-submit-url="/your-backend/submit-correction"
data-task-id="619"
data-form-id="F-100"></div>
<script src="https://learnosity.paperscorer.com/embed/dist/correction.{{ hash }}.js"></script>
Option B — Programmatic mount
For full control over the fetch (e.g. you already have
the sheet data in your app's store, or you need custom
request headers / retry logic), call
window.PSCorrectionEmbed.mount(el, options)
yourself.
<div id="my-correction"></div>
<script src="https://learnosity.paperscorer.com/embed/dist/correction.{{ hash }}.js"></script>
<script>
(async () => {
const data = await fetch('/your-backend/correction-data?task_id=619&form_id=F-100')
.then(r => r.json());
window.PSCorrectionEmbed.mount(
document.getElementById('my-correction'),
{
data,
async onSubmit(corrected) {
const res = await fetch('/your-backend/submit-correction', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
task_id: '619',
form_id: 'F-100',
...corrected,
}),
});
return res.json();
},
}
);
})();
</script>
Next steps
-
Read the
full integration guide
— auth model, data attribute reference, full
mount()API. - Review the request / response shapes for /get-correction-sheet and /submit-correction-sheet — your backend handles those.
- Need API access? Email hi@paperscorer.com.