A resume QR code does not contain your resume. It contains a link, and scanning it opens whatever sits at that link. So the job has two parts: give your resume a link, then print that link as a code on the paper copy.
If you searched for how to create a link for your resume, the first part is below. The second part takes a minute. Someone at a career-fair table scans the corner of your sheet, and your live page opens on their phone.

Start with the page the code will open. This one is written for a phone: who you are, what you want, and details that open on tap.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Taylor Morgan - Resume</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1f2430; line-height: 1.5; }
.wrap { max-width: 680px; margin: 0 auto; padding: 18px 16px 30px; }
header { background: #fff; border-radius: 14px; padding: 20px 20px 16px; box-shadow: 0 2px 10px rgba(0,0,0,.07); }
.avatar { width: 52px; height: 52px; border-radius: 50%; background: linear-gradient(135deg, #1d4ed8, #7c3aed); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 20px; float: left; margin-right: 14px; }
h1 { margin: 0; font-size: 24px; line-height: 1.2; }
.role { margin: 2px 0 0; font-size: 14px; color: #4b5563; }
.want { clear: both; margin: 14px 0 0; padding: 10px 12px; background: #eef4ff; border-radius: 10px; font-size: 14px; }
.links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.links a { padding: 8px 12px; border-radius: 99px; border: 1px solid #c7cfdd; font-size: 13px; color: #1d4ed8; text-decoration: none; background: #fff; }
section { background: #fff; border-radius: 14px; padding: 14px 20px; margin-top: 12px; box-shadow: 0 2px 10px rgba(0,0,0,.07); }
h2 { margin: 0 0 8px; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: #1d4ed8; }
.item { padding: 8px 0; border-top: 1px solid #eceff4; }
.item:first-of-type { border-top: 0; }
.item b { font-size: 15px; }
.meta { font-size: 12.5px; color: #6b7280; }
.more { display: none; margin: 6px 0 0; font-size: 13.5px; }
.item.open .more { display: block; }
button.toggle { margin-top: 4px; padding: 0; border: 0; background: none; color: #1d4ed8; font-size: 13px; cursor: pointer; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chips span { padding: 4px 10px; border-radius: 99px; background: #eef1f6; font-size: 13px; }
footer { margin-top: 12px; font-size: 12px; color: #6b7280; text-align: center; }
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="avatar">TM</div>
<h1>Taylor Morgan</h1>
<p class="role">Business student, Northfield College, class of 2027</p>
<p class="want"><b>Looking for:</b> a summer 2027 marketing or analytics internship.</p>
<div class="links">
<a href="mailto:taylor.morgan@example.com">Email me</a>
<a href="#projects">Projects</a>
<a href="#skills">Skills</a>
</div>
</header>
<section id="experience">
<h2>Experience</h2>
<div class="item">
<b>Marketing intern</b>
<div class="meta">Harbor Coffee Co. · Summer 2026</div>
<button class="toggle" type="button">Show details</button>
<p class="more">Wrote the weekly customer email and set up a sign-up flyer for the counter. Tracked each email's opens in a spreadsheet and reported the results every Friday.</p>
</div>
<div class="item">
<b>Treasurer</b>
<div class="meta">Student Marketing Club · 2025 to now</div>
<button class="toggle" type="button">Show details</button>
<p class="more">Kept the club budget for four events and posted a one-page summary after each one.</p>
</div>
</section>
<section id="projects">
<h2>Projects</h2>
<div class="item">
<b>Campus coffee survey</b>
<div class="meta">Course project · 60 responses</div>
<button class="toggle" type="button">Show details</button>
<p class="more">Designed a ten-question survey, cleaned the answers in a spreadsheet and presented three findings to the class.</p>
</div>
</section>
<section id="skills">
<h2>Skills</h2>
<div class="chips"><span>Spreadsheets</span><span>Survey design</span><span>Basic HTML</span><span>Presenting</span></div>
</section>
<footer>Last updated October 2026</footer>
</div>
<script>
// Each "Show details" button opens the notes under its own item
document.querySelectorAll('.toggle').forEach((btn) => {
btn.addEventListener('click', () => {
const item = btn.closest('.item');
const open = item.classList.toggle('open');
btn.textContent = open ? 'Hide details' : 'Show details';
});
});
</script>
</body>
</html>
The details are fictional. Replace them with your own, and keep the "Looking for" line, because it answers the first question a reader has.
Make the page with AI
Any of ChatGPT, Claude or Gemini can write this page. Paste the prompt below, replace the brackets with your facts, and ask for one file.
Write one self-contained HTML file: a one-screen resume page for a
student, laid out for a phone first. Use only inline CSS and a little
JavaScript, with no outside images, fonts or scripts. Use initials in
a coloured circle instead of a photo.
Top: my name, school and graduation year, and one line starting
"Looking for:". Then Experience, Projects and Skills. Each experience
or project shows a title and one line, with a Show details button that
opens two sentences.
My facts: [name, school, year, what you want, 2 jobs or clubs,
1 project, 4 skills]. Do not invent anything I did not give you.
Read the result before you use it. Check every date and name, and make sure nothing was added that you did not do.
Remove any phone number or home address you would not hand to a stranger. Printed codes get scanned by people you have never met.
Send it as a link
Paste the finished HTML into a NOS document and choose Create share link. HTML to link walks through it. The page renders as written, and the scripts in it run.
Whoever has the link opens it in a browser, on a phone too, with no account. They can read the page but cannot edit it. If you fix a typo or add a job later, the same link shows the new version.
Keep to one document. A fresh share link is a new address, so a code printed from the old one would still open the old page.
Pictures need care. As of October 2026, NOS blocks images loaded from other sites, so use inline SVG or CSS shapes, as the example does. For a wider look at sending a resume this way, see Share your resume as a link.
Put the code on the paper resume
The sheet below leaves a spot in the top corner. Type your share link, press Make QR code, and the square appears. The Print button opens your browser's print dialog, and a print rule hides the link bar so only the sheet prints.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Paper resume with a QR code</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 14px; font-family: system-ui, sans-serif; background: #eef0f4; color: #1f2430; }
.bar { max-width: 640px; margin: 0 auto 12px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.bar label { font-size: 13px; font-weight: 600; width: 100%; }
.bar input { flex: 1 1 220px; min-width: 0; padding: 9px 10px; font-size: 14px; border: 1px solid #b9bfcb; border-radius: 8px; }
.bar button { padding: 9px 14px; font-size: 14px; border: 0; border-radius: 8px; background: #1d4ed8; color: #fff; cursor: pointer; }
.msg { font-size: 12.5px; color: #9a3412; width: 100%; min-height: 16px; }
.sheet { max-width: 640px; margin: 0 auto; background: #fff; padding: 26px 28px; box-shadow: 0 3px 14px rgba(0,0,0,.12); }
.head { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; border-bottom: 2px solid #1f2430; padding-bottom: 12px; }
h1 { margin: 0; font-size: 26px; }
.sub { margin: 4px 0 0; font-size: 13px; color: #4b5563; }
.qr { text-align: center; flex: none; width: 112px; }
.qr .code { background: #fff; padding: 8px; border: 1px solid #d1d5db; line-height: 0; }
.qr svg { width: 96px; height: 96px; display: block; }
.qr .ph { width: 96px; height: 96px; display: grid; place-items: center; font-size: 11px; line-height: 1.3; color: #6b7280; }
.qr .cap { font-size: 11px; line-height: 1.3; margin-top: 5px; font-weight: 600; }
h2 { font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: #1d4ed8; margin: 16px 0 5px; }
p, li { font-size: 13px; line-height: 1.5; margin: 0 0 4px; }
ul { margin: 0; padding-left: 18px; }
@media (max-width: 480px) { .sheet { padding: 18px 16px; } h1 { font-size: 22px; } .head { flex-direction: column-reverse; align-items: stretch; } .qr { align-self: flex-start; } }
@media print { body { background: #fff; padding: 0; } .bar { display: none; } .sheet { box-shadow: none; } }
</style>
</head>
<body>
<div class="bar">
<label for="url">Your resume page link</label>
<input id="url" type="url" inputmode="url" value="https://example.com/your-resume" autocomplete="off">
<button id="make" type="button">Make QR code</button>
<button id="print" type="button">Print</button>
<div class="msg" id="msg"></div>
</div>
<div class="sheet">
<div class="head">
<div>
<h1>Taylor Morgan</h1>
<p class="sub">Business student, class of 2027 · Marketing and data analysis</p>
</div>
<div class="qr">
<div class="code" id="code"><div class="ph">Press Make QR code</div></div>
<div class="cap">Scan for my live resume</div>
</div>
</div>
<h2>Education</h2>
<p><b>B.A. Business Administration</b>, Northfield College, expected 2027</p>
<h2>Experience</h2>
<ul>
<li><b>Marketing intern</b>, Harbor Coffee Co. (summer 2026): wrote the weekly email, grew the list with a sign-up flyer</li>
<li><b>Treasurer</b>, Student Marketing Club: kept a budget of a few hundred dollars for four events</li>
</ul>
<h2>Skills</h2>
<p>Spreadsheets, survey design, basic HTML, presenting to small groups</p>
</div>
<script>
// Minimal QR encoder: byte mode, error correction level M, versions 1-10.
const EC = { // [ec codewords per block, blocks in group 1, data cw group 1, blocks group 2, data cw group 2] for level M
1:[10,1,16,0,0], 2:[16,1,28,0,0], 3:[26,1,44,0,0], 4:[18,2,32,0,0], 5:[24,2,43,0,0],
6:[16,4,27,0,0], 7:[18,4,31,0,0], 8:[22,2,38,2,39], 9:[22,3,36,2,37], 10:[26,4,43,1,44]
};
const ALIGN = { 1:[], 2:[6,18], 3:[6,22], 4:[6,26], 5:[6,30], 6:[6,34], 7:[6,22,38], 8:[6,24,42], 9:[6,26,46], 10:[6,28,50] };
const EXP = [], LOG = [];
(function () { let x = 1; for (let i = 0; i < 255; i++) { EXP[i] = x; LOG[x] = i; x <<= 1; if (x & 256) x ^= 0x11d; } for (let i = 255; i < 512; i++) EXP[i] = EXP[i - 255]; })();
const gmul = (a, b) => (a && b) ? EXP[LOG[a] + LOG[b]] : 0;
function rsRemainder(data, n) {
let gen = [1];
for (let i = 0; i < n; i++) {
const next = new Array(gen.length + 1).fill(0);
for (let j = 0; j < gen.length; j++) { next[j] ^= gen[j]; next[j + 1] ^= gmul(gen[j], EXP[i]); }
gen = next;
}
const rem = new Array(n).fill(0);
for (const b of data) {
const f = b ^ rem.shift(); rem.push(0);
for (let i = 0; i < n; i++) rem[i] ^= gmul(gen[i + 1], f);
}
return rem;
}
function bchFormat(data) { let r = data; for (let i = 0; i < 10; i++) r = (r << 1) ^ ((r >>> 9) * 0x537); return ((data << 10) | r) ^ 0x5412; }
function bchVersion(v) { let r = v; for (let i = 0; i < 12; i++) r = (r << 1) ^ ((r >>> 11) * 0x1f25); return (v << 12) | r; }
function encodeQR(text) {
const bytes = Array.from(new TextEncoder().encode(text));
let ver = 0;
for (let v = 1; v <= 10; v++) {
const e = EC[v], cap = e[1] * e[2] + e[3] * e[4];
const ccBits = v < 10 ? 8 : 16;
if (4 + ccBits + bytes.length * 8 <= cap * 8) { ver = v; break; }
}
if (!ver) return null;
const e = EC[ver], cap = e[1] * e[2] + e[3] * e[4];
const bits = [];
const put = (val, n) => { for (let i = n - 1; i >= 0; i--) bits.push((val >>> i) & 1); };
put(4, 4); put(bytes.length, ver < 10 ? 8 : 16);
bytes.forEach(b => put(b, 8));
put(0, Math.min(4, cap * 8 - bits.length));
while (bits.length % 8) bits.push(0);
for (let pad = 0xec; bits.length < cap * 8; pad ^= 0xec ^ 0x11) put(pad, 8);
const data = [];
for (let i = 0; i < bits.length; i += 8) data.push(parseInt(bits.slice(i, i + 8).join(''), 2));
const blocks = []; let pos = 0;
for (let g = 0; g < 2; g++) for (let k = 0; k < e[1 + g * 2]; k++) { const len = e[2 + g * 2]; blocks.push(data.slice(pos, pos + len)); pos += len; }
const ecs = blocks.map(b => rsRemainder(b, e[0]));
const out = [];
const maxLen = Math.max(...blocks.map(b => b.length));
for (let i = 0; i < maxLen; i++) blocks.forEach(b => { if (i < b.length) out.push(b[i]); });
for (let i = 0; i < e[0]; i++) ecs.forEach(c => out.push(c[i]));
const stream = []; out.forEach(b => put2(stream, b));
function put2(arr, val) { for (let i = 7; i >= 0; i--) arr.push((val >>> i) & 1); }
const size = ver * 4 + 17;
const m = Array.from({ length: size }, () => new Array(size).fill(false));
const fn = Array.from({ length: size }, () => new Array(size).fill(false));
const set = (x, y, v) => { m[y][x] = v; fn[y][x] = true; };
const finder = (cx, cy) => {
for (let dy = -4; dy <= 4; dy++) for (let dx = -4; dx <= 4; dx++) {
const x = cx + dx, y = cy + dy; if (x < 0 || y < 0 || x >= size || y >= size) continue;
const d = Math.max(Math.abs(dx), Math.abs(dy)); set(x, y, d !== 2 && d !== 4);
}
};
finder(3, 3); finder(size - 4, 3); finder(3, size - 4);
for (let i = 8; i < size - 8; i++) { set(i, 6, i % 2 === 0); set(6, i, i % 2 === 0); }
const al = ALIGN[ver];
al.forEach((ay, i) => al.forEach((ax, j) => {
if ((i === 0 && j === 0) || (i === 0 && j === al.length - 1) || (i === al.length - 1 && j === 0)) return;
for (let dy = -2; dy <= 2; dy++) for (let dx = -2; dx <= 2; dx++) set(ax + dx, ay + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
}));
set(8, size - 8, true);
for (let i = 0; i < 9; i++) { fn[8][i] = true; fn[i][8] = true; }
for (let i = 0; i < 8; i++) { fn[8][size - 1 - i] = true; fn[size - 1 - i][8] = true; }
if (ver >= 7) {
const vb = bchVersion(ver);
for (let i = 0; i < 18; i++) {
const bit = ((vb >>> i) & 1) === 1, a = size - 11 + (i % 3), b = Math.floor(i / 3);
set(a, b, bit); set(b, a, bit);
}
}
let k = 0;
const place = (mask) => {
const g = m.map(r => r.slice()); k = 0;
for (let right = size - 1; right >= 1; right -= 2) {
if (right === 6) right = 5;
for (let v = 0; v < size; v++) for (let j = 0; j < 2; j++) {
const x = right - j, up = ((right + 1) & 2) === 0, y = up ? size - 1 - v : v;
if (fn[y][x]) continue;
let bit = k < stream.length ? stream[k] === 1 : false; k++;
const inv = [(x + y) % 2 === 0, y % 2 === 0, x % 3 === 0, (x + y) % 3 === 0, (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0, (x * y) % 2 + (x * y) % 3 === 0, ((x * y) % 2 + (x * y) % 3) % 2 === 0, ((x + y) % 2 + (x * y) % 3) % 2 === 0][mask];
g[y][x] = bit !== inv;
}
}
const f = bchFormat((0 << 3) | mask); // level M = 00
const fb = i => ((f >>> i) & 1) === 1;
for (let i = 0; i <= 5; i++) g[i][8] = fb(i);
g[7][8] = fb(6); g[8][8] = fb(7); g[8][7] = fb(8);
for (let i = 9; i < 15; i++) g[8][14 - i] = fb(i);
for (let i = 0; i < 8; i++) g[8][size - 1 - i] = fb(i);
for (let i = 8; i < 15; i++) g[size - 15 + i][8] = fb(i);
g[size - 8][8] = true;
return g;
};
const penalty = (g) => {
let p = 0;
for (let t = 0; t < 2; t++) {
for (let a = 0; a < size; a++) {
let run = 1;
for (let b = 1; b < size; b++) {
const cur = t ? g[b][a] : g[a][b], prev = t ? g[b - 1][a] : g[a][b - 1];
if (cur === prev) { run++; if (run === 5) p += 3; else if (run > 5) p++; } else run = 1;
}
}
}
for (let y = 0; y < size - 1; y++) for (let x = 0; x < size - 1; x++) if (g[y][x] === g[y][x + 1] && g[y][x] === g[y + 1][x] && g[y][x] === g[y + 1][x + 1]) p += 3;
let dark = 0; g.forEach(r => r.forEach(v => { if (v) dark++; }));
p += Math.floor(Math.abs(dark * 20 - size * size * 10) / (size * size)) * 10;
return p;
};
let best = null, bestP = Infinity;
for (let mask = 0; mask < 8; mask++) { const g = place(mask); const p = penalty(g); if (p < bestP) { bestP = p; best = g; } }
return best;
}
function toSvg(grid) {
const n = grid.length, q = 4; // quiet zone: 4 modules
let d = '';
grid.forEach((row, y) => row.forEach((v, x) => { if (v) d += 'M' + (x + q) + ' ' + (y + q) + 'h1v1h-1z'; }));
const t = n + q * 2;
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + t + ' ' + t + '" shape-rendering="crispEdges"><rect width="' + t + '" height="' + t + '" fill="#fff"/><path d="' + d + '" fill="#000"/></svg>';
}
const url = document.getElementById('url'), msg = document.getElementById('msg'), code = document.getElementById('code');
function make() {
const text = url.value.trim();
msg.textContent = '';
if (!/^https?:\/\//i.test(text)) { msg.textContent = 'Start the link with https://'; return; }
const grid = encodeQR(text);
if (!grid) { msg.textContent = 'That link is too long for this small generator. Use a shorter one.'; return; }
code.innerHTML = toSvg(grid);
}
document.getElementById('make').addEventListener('click', make);
document.getElementById('print').addEventListener('click', () => window.print());
url.addEventListener('keydown', e => { if (e.key === 'Enter') make(); });
make();
</script>
</body>
</html>
This small generator runs inside the page, and it needs no account. Its output was decoded with OpenCV's QR reader on a set of sample links during testing. Even so, scan your own printout before you copy a stack.
You can also make the code in Chrome. Chrome Help describes a Create QR code option for the page you have open, with a Download button for the image.
Where it sits in the menu can differ between Chrome versions, so check the Help page for yours.

Three habits help the code scan:
- Leave a margin. The Denso Wave QR Code site says a margin is required around the code. Put the code in its own white box.
- Add a few words. "Scan for my live resume" tells a stranger why to raise a phone.
- Keep the link short. More data means more modules, and a larger code. A shorter link gives a simpler square.
More on sizing and placement is in QR code link.
What the employer does with it
At a table, the employer holds your paper resume. They open the phone's camera, point it at the square, and tap what appears.

Apple's support page says the iPhone Camera recognises the code, shows a notification, and opens the link when tapped.
Android's site describes the same: open the camera, point it at the code, and a notification with the details appears. Both are as of October 2026, and settings differ between phones.
Paper, link or code: which to print
You do not have to choose. The sheet can carry all three, and each covers a different reader.
| What you print | Who it helps | The catch |
|---|---|---|
| The page's address as text | Anyone who types it | Each character is typed by hand |
| A QR code | Someone holding a phone with a camera | Needs a clean print and a margin |
| Both, plus your email | Every reader | Uses a little more space on the sheet |
| Only the paper text | Someone who reads on paper | The text is frozen once printed |
For a first reply, an email address still does the work. The code is for the person who wants to see more.
When it does not work
| What you see | Likely cause | Fix |
|---|---|---|
| The camera shows nothing | Code too small, blurred or cut off | Print it larger, give it a blank margin, and re-scan |
| The code opens an old version | A new share link was made, not an edit | Edit the original document and reprint only if the link changed |
| The page is tiny and zoomed out | The page has no viewport tag | Add the viewport meta tag |
| A picture is missing on the page | The image came from another site | Use inline SVG or a CSS shape |
| The code is a dense grid | The link is long | Use a shorter link and print the code bigger |
| Only some phones scan it | Camera settings differ | Print the link as text under the code |
Last, test the printed sheet. Scan it with your own phone and with a friend's, in the room's light, from the distance of a table.