Enhance lecture slides (PPTX, PDF, or HTML) into interactive Canvas-based learning experiences with step-through animations, playgrounds, and challenge quizzes. Accepts any source format. Can also generate standalone exercise apps.
Transform lecture slides from any format (PPTX, PDF, or existing HTML) into interactive, self-contained HTML decks with Canvas visualizations, step-through animations, and challenge quizzes.
Target file: $0
Focus area: $1 (if not specified, analyze the entire source and propose a 3-part split)
Detect the input file type and extract content accordingly:
| Input | Action |
|---|---|
*.pptx | Extract text from each slide → build enhanced deck directly |
*.pdf | Read pages in chunks of ~10 → extract content → build enhanced deck directly |
*-explained.html |
| Existing HTML deck → analyze and enhance (original flow) |
*-enhanced.html + part N | Resume building an in-progress enhanced deck |
Any .html | Analyze structure → enhance |
pages parameter: pages: "1-10", then "11-20", etc.After extraction, plan the enhanced deck:
.analogy callout).warning callout).key-idea callout)sorting.pptx → Output: sorting-enhanced.htmlchapter5.pdf → Output: chapter5-enhanced.htmltrees-explained.html → Output: trees-enhanced.htmlNever skip planning or build without approval:
Each part is a checkpoint. Never combine parts. Always explain the "why" behind design choices.
Large decks (15+ original slides) are always split into 3 parts for incremental review:
<!-- ==================== NAVIGATION ==================== --> through const slideOrder = [...] with new slide HTML + updated nav + extended slideOrder.Before each edit: Always Grep for both <!-- ==================== NAVIGATION ==================== and const slideOrder to confirm exact line numbers.
| Content Type | Enhancement Pattern | Priority |
|---|---|---|
| Algorithm walkthrough (step-by-step) | Step-through — Next/Auto/Reset with Canvas animation | HIGH |
| Data structure operations (insert, remove) | Interactive playground — user inputs values, watches animated ops | HIGH |
| Code example | Code block with .line.active synced to step-through | HIGH |
| Comparison (e.g., sorted vs unsorted list) | Side-by-side Canvas with toggle or "Run Both" button | HIGH |
| Application (Top-K, merge, scheduling) | Interactive simulation — configurable input, animated execution | MEDIUM |
| After a teaching section (every 2-3 slides) | Inline challenge (sCA, sCB, sCC) | MEDIUM |
| Concept definition or properties | Canvas diagram with interactive hover/click exploration | MEDIUM |
| Summary / cheat sheet | Canvas table with color-coded costs + decision flowchart | LOW |
| Pure definition or formula | Keep mostly static with .key-idea / .warning / .analogy callouts | LOW |
When the source deck has 2-4 consecutive slides showing the same operation (e.g., "Insert Step 1", "Insert Step 2", "Insert Step 3"), merge them into a single interactive slide with:
This typically reduces 20 source slides to ~16-18 content slides, making room for 6 challenge slides within a 24-slide target.
Present a numbered plan:
Ask for approval, then build one part at a time.
<!DOCTYPE html>
<html lang="en">
<head><!-- meta, title, full CSS --></head>
<body>
<div id="progress-bar"><div id="progress"></div></div>
<!-- SLIDES (each is <div class="slide" id="sN">) -->
<!-- Inline <script> blocks after each interactive slide -->
<!-- ==================== NAVIGATION ==================== -->
<div class="nav">...</div>
<!-- ==================== NAVIGATION JS ==================== -->
<script>
const slideOrder = ['s1','s2',...];
// showSlide, navigate, keyboard handler, init
</script>
</body>
</html>
s1, s2, ..., s20sCA, sCB, sCCsQ1, sQ2, sQ3Challenges are interleaved with content, not grouped at the end:
const slideOrder = ['s1','s2',...,'s8', // Part 1
's9','s10','s11', // Part 2 content
's12','s13','sCA','s14','sCB', // challenges between content
's15','sCC','s16', // Part 3 content
's17','s18','sQ1','sQ2','sQ3']; // final quiz
Background: #0f172a
Panel background: #1e293b, rgba(0,0,0,0.2) for canvas bg, rgba(0,0,0,0.25) for logs
Borders: #334155, #475569
Text primary: #e2e8f0
Text secondary: #94a3b8, #cbd5e1
Code font: monospace
Colors:
Blue: #38bdf8 (headings), #6366f1 (nodes/primary), #818cf8 (borders)
Purple: #a78bfa (accents)
Amber: #f59e0b (highlights, active items)
Green: #22c55e (success, sorted, correct)
Red: #ef4444 (errors, wrong answers)
Pink: #f472b6 (secondary accent)
.btn — gradient blue-purple button (background: linear-gradient(135deg, #6366f1, #8b5cf6))
.btn-sm — smaller padding variant
.btn-secondary — gray button (#334155 bg)
.key-idea — green-bordered callout box
.warning — red-bordered callout box
.analogy — purple-bordered callout box
.code-block — dark code container with .code-content inside
.line — single code line (inside .code-content)
.line.active — highlighted code line (rgba(99,102,241,0.15) bg + left border)
.slide-number — empty div at bottom of each slide (populated by CSS or left empty)
width="520" height="370" (or similar), with style="width:100%" for responsivenessstyle="background:rgba(0,0,0,0.2);border-radius:12px;"Tree drawing pattern:
for (let i = 0; i < n; i++) {
const lv = Math.floor(Math.log2(i + 1));
const posInLv = i - Math.pow(2, lv) + 1;
const spacing = canvasWidth / Math.pow(2, lv);
const x = spacing/2 + posInLv * spacing;
const y = startY + lv * levelGap;
}
Graph drawing pattern (weighted, directed):
// Node positions — precompute as {id: {x, y}} object
const positions = { A: {x:80, y:60}, B: {x:220, y:60}, ... };
// Draw edges first (so nodes overlap them)
edges.forEach(({from, to, weight}) => {
const p1 = positions[from], p2 = positions[to];
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = highlighted ? '#f59e0b' : '#475569';
ctx.lineWidth = highlighted ? 3 : 1.5;
ctx.stroke();
// Weight label at midpoint
const mx = (p1.x + p2.x) / 2, my = (p1.y + p2.y) / 2;
ctx.fillStyle = '#cbd5e1';
ctx.fillText(weight, mx, my - 6);
// Arrowhead for directed graphs
});
// Draw nodes on top
Object.entries(positions).forEach(([id, {x, y}]) => {
ctx.beginPath();
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.fillStyle = visited.has(id) ? '#22c55e' : current === id ? '#f59e0b' : '#6366f1';
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px monospace';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(id, x, y);
});
Distance/state table on Canvas:
// Draw a table below or beside the graph
const cols = nodes.length;
const cellW = 50, cellH = 28, tableX = 20, tableY = canvasHeight - 70;
// Header row
nodes.forEach((n, i) => {
ctx.fillStyle = '#94a3b8';
ctx.fillText(n, tableX + i * cellW + cellW/2, tableY);
});
// Value row
dist.forEach((d, i) => {
ctx.fillStyle = d === Infinity ? '#64748b' : '#e2e8f0';
ctx.fillText(d === Infinity ? '∞' : d, tableX + i * cellW + cellW/2, tableY + cellH);
});
Comparison toggle (side-by-side or run-both):
// Two canvases side by side, or one canvas with a toggle button
window.sNToggle = function(mode) {
currentMode = mode;
// Re-draw canvas with different algorithm/approach
draw();
};
// "Run Both" button — runs two animations simultaneously on split canvas
IIFE encapsulation — every slide's JS is wrapped in an IIFE to avoid global pollution:
(function(){
const canvas = document.getElementById('cS5');
const ctx = canvas.getContext('2d');
// Only expose button handlers to global scope:
window.s5Step = function() { ... };
window.s5Reset = function() { ... };
window.s5Auto = function() { ... };
// MutationObserver to reinitialize when slide becomes active:
const obs = new MutationObserver(() => {
if (document.getElementById('s5').classList.contains('active')) init();
});
obs.observe(document.getElementById('s5'), {attributes: true, attributeFilter: ['class']});
init();
})();
Naming conventions:
cS5, cS12, cCA (c + slide ID)s5Step(), s5Reset(), s5Auto() (slide ID + action)s5Log, s5Statustimer (local to IIFE), always cleared in resetStep-through pattern:
let steps = [...]; // precomputed
let stepIdx = 0;
let timer = null;
window.sNStep = function() {
if (stepIdx >= steps.length) return;
// apply step, highlight, update canvas
stepIdx++;
};
window.sNAuto = function() {
if (timer) return;
timer = setInterval(() => {
if (stepIdx >= steps.length) { clearInterval(timer); timer = null; return; }
sNStep();
}, 800);
};
window.sNReset = function() {
if (timer) clearInterval(timer); timer = null;
stepIdx = 0;
init();
};
Interactive playground pattern (user enters value, animated operation):
window.sNInsert = function() {
const val = parseInt(document.getElementById('sNinput').value);
if (isNaN(val)) return;
// add to data structure
// animate with requestAnimationFrame or setTimeout chain
};
Interactive input pattern (user adjusts values, sees result immediately):
// For edge relaxation, comparison inputs, etc.
window.sNCheck = function() {
const a = parseInt(document.getElementById('sNvalA').value);
const b = parseInt(document.getElementById('sNvalB').value);
// compute result, update Canvas, show explanation
};
upheap(i) — bubble up, swapping with parentdownheap(i) — sink down, swapping with smaller/larger childbuildMinHeap() / buildMaxHeap() — bottom-up heapifyhighlight array of indices to draw function{A: {x, y}, B: {x, y}, ...} — precomputed layout{A: [{to:'B', w:3}, ...], ...}const slideOrder = ['s1','s2',...];
let currentIdx = 0;
function showSlide(idx) {
slideOrder.forEach(id => document.getElementById(id).classList.remove('active'));
const slideEl = document.getElementById(slideOrder[idx]);
slideEl.classList.add('active');
slideEl.classList.remove('fade-in');
void slideEl.offsetWidth; // force reflow
slideEl.classList.add('fade-in');
document.getElementById('prevBtn').disabled = (idx === 0);
document.getElementById('nextBtn').disabled = (idx === slideOrder.length - 1);
const pct = slideOrder.length > 1 ? (idx / (slideOrder.length - 1)) * 100 : 0;
document.getElementById('progress').style.width = pct + '%';
}
function navigate(dir) { /* bounds-checked */ }
// Keyboard: ArrowLeft/Right for nav, DISABLED when INPUT/SELECT/TEXTAREA focused
document.addEventListener('keydown', (e) => {
const tag = document.activeElement.tagName;
if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') return;
if (e.key === 'ArrowRight') navigate(1);
if (e.key === 'ArrowLeft') navigate(-1);
});
showSlide(0);
sCA — Trace/Predict (inline after a concept section):
sCB — Fix the Bug (inline after a code-heavy section):
<select> to identify the bugsCC — Decision/Scenario (inline after comparison section):
sQ1 — Multi-Question Quiz (final section):
sQ2 — Trace Exercise (final section):
sQ3 — Predict Output (final section):
<!-- Two-column: visualization + controls -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;margin-top:1rem;">
<div><!-- Canvas or code --></div>
<div><!-- Controls, log, explanation --></div>
</div>
<!-- Three-column: quiz questions -->
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;margin-top:1rem;">
<!-- Log/output area -->
<div style="background:rgba(0,0,0,0.25);border-radius:8px;padding:0.6rem;
font-family:monospace;font-size:0.78rem;max-height:120px;overflow-y:auto;">
<!-- Control button row -->
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;">
<button class="btn btn-sm" onclick="sNStep()">Step</button>
<button class="btn btn-sm" onclick="sNAuto()">Auto Play</button>
<button class="btn btn-sm btn-secondary" onclick="sNReset()">Reset</button>
</div>
<!-- Input with label -->
<input type="text" id="sNinput" value="default"
style="padding:0.4rem 0.6rem;background:#1e293b;border:1px solid #475569;
border-radius:6px;color:#e2e8f0;font-family:monospace;">
When adding Part 2 or Part 3 to an existing file:
<!-- ==================== NAVIGATION ==================== to find the line numberconst slideOrder to confirm the slideOrder line<div> blocks + their <script> blocksWhen Q asks to create a practice exercise (not a slide deck), use a different architecture:
Fixed header: progress bar + timer + live score
Scrollable body: sections of questions
Summary panel: appears when all questions answered
| Aspect | Slide Deck | Exercise App |
|---|---|---|
| Navigation | Arrow keys, prev/next buttons | Scroll (all visible) |
| Layout | One slide visible at a time | All questions visible, sectioned |
| State | Per-slide IIFE | Centralized scoring (Set-based) |
| Feedback | Step-through reveals | Instant per-question check |
| Canvas | Heavy (every interactive slide) | Light or none (CSS feedback) |
| Timer | None | Countdown with color warning |
.opt.selected class, color-coded feedback.input-answer field, exact match or flexible validation<select> dropdowns, check-all button// Centralized state
const totalQs = 18;
let answered = new Set();
let correct = new Set();
function markQuestion(qid, isCorrect) {
answered.add(qid);
if (isCorrect) correct.add(qid);
updateProgress();
}
// Prevent re-answering
function checkMC(qid) {
if (answered.has(qid)) return;
// ... validate, show feedback, markQuestion
}
// Countdown timer (color turns red at 5 min)
let timeLeft = duration * 60;
const timerInterval = setInterval(() => {
if (timeLeft <= 0) { clearInterval(timerInterval); return; }
timeLeft--;
if (timeLeft <= 300) timerEl.style.color = '#ef4444';
}, 1000);
// Summary with screenshot instructions
function showSummary() {
// Section-by-section breakdown
// Name display for identification
// Screenshot instructions for eCampus submission
}
.question — card with left border, margin-bottom
.question.correct — green border
.question.incorrect — red border
.question.answered — slightly dimmed
.q-num — colored badge (different color per section)
.hint / .hint.show — toggleable hint box
.feedback.correct — green text
.feedback.incorrect — red text
.order-item — clickable draggable item
.order-slot — drop target with dashed border
.order-slot.filled — solid border, filled state
.key-idea / .warning / .analogy callouts. Only add interactivity where it genuinely helps understanding.window.sNAction button handlers.window.sNAction handlers.line.active highlighting syncs with step index.btn / .btn-sm / .btn-secondary styling.code-block containersAll 15 enhanced decks are in the working directory:
linked-list-enhanced.html (27 slides)algorithm-analysis-enhanced.html (24 slides)recursion-enhanced.html (24 slides)stacks-enhanced.html (23 slides)queues-enhanced.html (23 slides)arraylists-enhanced.html (23 slides)priority-queues-enhanced.html (24 slides)heap-enhanced.html (26 slides)maps-enhanced.htmlhashtables-enhanced.htmltrees-enhanced.htmlgraph-enhanced.htmlbfs-enhanced.htmldfs-enhanced.htmlshortest-path-enhanced.html (24 slides)Exercise apps:
exercise-lists-arrays-complexity.html (18 questions, 50 min)Use any of these as reference when building new decks.