An AI-assisted Canvas course builder. Upload a Canvas course template, describe your course, get an .imscc ready to import.
A fork of ClassBuild by Jason Tangen, retargeted at instructors who need to ship Canvas-shaped courses fast: takes a Canvas course export (.imscc) as a structural template, generates AI content for each module that matches the template's pattern, and produces a fresh .imscc for re-import.
Compatible with Canvas LMS — not affiliated with Instructure, Inc. Canvas® is a registered trademark of Instructure, Inc.
| Capability | Where it lives |
|---|---|
Upload a Canvas .imscc template, parse the module structure, classify modules as verbatim / pattern / example-pattern |
/templates page |
| Generate per-chapter Canvas Module content (1× Module Overview, 1+ MN Instructor Notes, 1× MN Discussion) | "Canvas Module" tab on Build |
| Batch generation: "Generate All Canvas Modules" for the whole course at once | Build header |
Export an .imscc that mirrors the template's structure: verbatim modules carry through untouched, pattern modules replaced by generated content, LTI links + web_resources passed through |
"Export for Canvas (.imscc)" button |
Upload a course-outline .docx and have an LLM extract title / description / course information / course materials → populates Canvas's Syllabus tab body at export time |
Setup page |
| Pluggable LLM provider — Anthropic (Claude) or Ollama Cloud for course-content generation | Setup → Course-content provider |
| Pluggable research backend — Claude web search, Tavily, or Wikipedia | Setup → Research backend |
| Advanced-mode toggle — hides multimedia outputs (slides, audio, infographic, weekly challenge, activities) by default to keep the Canvas-focused happy path tight | Setup page bottom |
git clone https://github.com/daTechGuy/CanvasClassBuild.git
cd CanvasClassBuild
npm install
npm run devOpen localhost:5173.
Bring Your Own Key — keys are entered through the Setup page and never leave your browser. There is no server-side component except a Vite dev proxy that exists solely to bypass Ollama Cloud's missing browser CORS headers.
| Provider | Required when… | What it does |
|---|---|---|
| Anthropic Claude | LLM provider = Anthropic, OR research backend = Claude web search | Course-content generation; Claude's built-in web search for the Research stage |
| Ollama Cloud | LLM provider = Ollama Cloud | Course-content generation. Free tier doesn't include cloud models — see ollama.com/settings/keys |
| Tavily | Research backend = Tavily | Web search for the Research stage. Free tier covers ~1,000 searches/month |
| Google Gemini | Advanced mode + you want infographics or audiobook narration | TTS + image generation (optional) |
The Wikipedia research backend needs no key.
- Upload your Canvas course template. Visit
/templates, drop in an.imsccyou exported from a previous Canvas course. The parser identifies which modules to keep verbatim (Instructor Information, Begin Here / Introductory) and which are placeholder patterns to be replaced (Module 1, Module 2, …). - Setup. Enter the course topic and chapter count (defaults to 16 when a template is selected). Optionally upload a course-outline
.docx; an LLM extracts the title, description, course information, and materials so they populate the Canvas Syllabus tab at export time. - Syllabus. AI generates chapter titles in the locked
Module N: <topic>format. Edit the topic suffix per chapter from the inline editor — the locked prefix is preserved on save. - Research. Choose a backend. Claude web search is the highest-quality but Anthropic-only; Tavily and Wikipedia work with any LLM provider. Skippable.
- Build. For each chapter, click "Generate Canvas Module" (or batch with "Generate All Canvas Modules") to produce the Module Overview + Instructor Notes pages + Discussion. The locked
MN Instructor Notes:/MN Discussion:prefixes are added at export time. - Export. "Export for Canvas (.imscc)" routes through a template-aware exporter. Output: a fresh
.imscccontaining your verbatim modules, your generated Module N modules, all original LTI links, all original web_resources images, and a Syllabus tab body composed from the outline DOCX. Drop it into Canvas via Settings → Import Course Content → Common Cartridge Package.
The upstream ClassBuild also produces:
- Interactive HTML reading with embedded widgets
- Gamified practice quiz with confidence calibration
- In-class quiz (5 shuffled versions + answer keys)
- PowerPoint slides with speaker notes
- AI-narrated audiobook (Gemini TTS)
- AI-generated infographic (Gemini)
- Weekly mastery challenge with 6 question types and SCORM 2004 wrapper
- Discussion starters and classroom activities
These are hidden behind the "Advanced mode" toggle on Setup so the Canvas-focused workflow stays tight. Flip it on and the Build page surfaces all the additional tabs.
Vitest scaffold covering the parser + exporter critical paths. Network-free, runs in <1s.
npm test # run once
npm run test:watch # re-run on file change
npm run test:coverage # generate coverage report (HTML at coverage/index.html)CI runs test:coverage on every push and PR; the resulting coverage/
directory uploads as a workflow artifact (coverage-report, retained 14 days).
Baseline at the time of the test scaffold lands: ~22% lines / ~15% branches
across the instrumented modules (parser, exporters, research backends,
services, setup components). Pages — BuildPage, SyllabusPage,
ExportPage, ResearchPage — are not yet instrumented for unit tests.
Current coverage:
tests/template-parser.test.ts— module classification (verbatim / pattern / example-pattern), prefix detection (Module N:,MN Instructor Notes:, fully-lockedModule N Overview),(Example to Edit)placeholder marker,**EDIT**markers, example-pattern content extraction.tests/imscc-exporter.test.ts— manifest shape, course_settings extension files, reading webcontent, QTI 1.2 quiz emission (practice + in-class + weekly challenge), Canvas auto-publish sidecars, native discussion topics.tests/template-imscc-exporter.test.ts— round-trip: build template fixture → emit IMSCC → re-parse → verify verbatim modules preserved, pattern modules replaced,web_resources/+lti_resource_links/pass through. Outline-field overrides on title / syllabus body / manifest LOM.tests/parse-outline-docx.test.ts— outline-DOCX field extraction with the LLM mocked: clean JSON, code-fenced JSON, partial / empty / malformed responses, char-cap on long input, provider override forwarding.tests/generate-template-chapter.test.ts— Canvas Module generation with the LLM mocked: parse success / failure / missing-required-field, few-shot exemplar embedding, no-example case, Ollama provider plumbing, Anthropic Sonnet default.tests/components/TemplatePicker.test.tsx— empty state, listing uploaded templates, selecting a template setstemplateId+numChapters=16, "No template" doesn't touchnumChapters, active-template notice.tests/components/CourseOutlineUpload.test.tsx— empty preview, populated preview, Clear resets the store, textarea edits write through, blanking a field deletes the key.tests/components/TemplateTitleEditor.test.tsx— locked-prefix display, save reassemblesModule N: <suffix>with prefix intact, blanking the suffix preserves just the prefix, off-pattern titles get a synthesized prefix, Reset rolls drafts back.tests/research-wikipedia.test.ts— Wikipedia backend with LLM +fetchboth mocked: query gen → per-query API call → synthesis call ordering, URL builder + dedup,<span class="searchmatch">snippet stripping, per-query failure doesn't abort batch, progress phase transitions, fallback query when query-gen returns nothing parseable.tests/research-tavily.test.ts— Tavily backend with LLM +fetchboth mocked: missing-key throw, POST body shape (max_results,search_depth,include_answer) + bearer auth, cross-query dedup by URL, content snippets feed into synthesis prompt, hits without a URL are dropped, per-query failure doesn't abort batch, progress phase order,published_datepreserved aspageAge.tests/components/ApiKeyPanel.test.tsx— provider toggle (Claude/Ollama) writes to apiStore, research backend toggle (Claude / Tavily / Wikipedia), hint text changes per selection, Ollama model input visibility gated onprovider==='ollama' && key.trim() !== '', model edits write through, auto-validate on mount POSTs to the right endpoints with bearer auth (Ollama →/api/ollama-proxy, Tavily →api.tavily.com/search, Gemini →googleapis.com/.../models?key=…), non-2xx setskeyValid=false, no re-validation when already validated or key is blank.tests/research-anthropic.test.ts— Anthropic research backend withstreamWithRetrymocked: streamWithRetry called with the Claude key +web_search_20250305tool + forcedprovider:'anthropic', progress phases emitted inthinking → searching → compilingorder, queries appended viaappendQueries, streamed text appended viaappendSynthesisText, dedup by URL across batches,setLatestSourceis the last fresh hit, valid JSON parses into a dossier, malformed text falls back to a dossier built from collected web results.
The app is a static SPA, so any static host works. The one wrinkle is Ollama Cloud requires a serverless proxy: ollama.com doesn't set CORS headers, so direct browser fetches are blocked. A Vercel Edge function ships with the repo at api/ollama-proxy.ts — Vercel picks it up automatically. Other hosts:
- Vercel —
vercel deploy. The Edge function and the SPA rewrite invercel.jsoncome along for free. - Cloudflare Pages — port
api/ollama-proxy.tsto a Workers function (same logic, different runtime API). - Static-only host (Netlify static, GitHub Pages, S3) — Ollama selection won't work; users must pick Anthropic.
The browser always calls /api/ollama-proxy regardless of provider — the Vite dev server forwards in development, the Vercel function forwards in production.
Canvas course CLI (scripts/canvas-course.ts)
Headless equivalent of the UI's Canvas-template flow. Takes a topic + a template .imscc (and optionally an outline .docx), generates a syllabus + Canvas Module content for each chapter, and writes a fresh .imscc ready for Canvas import.
ANTHROPIC_API_KEY=sk-... npx tsx scripts/canvas-course.ts \
--topic "Statistics for Data Science" \
--template ./template.imscc \
--outline ./outline.docx \
--chapters 16 \
--output ./output/statsFlags:
| Flag | Default | Purpose |
|---|---|---|
--topic |
required | Course topic |
--template |
required | Path to a Canvas .imscc template export |
--outline |
— | Path to a course-outline .docx (populates the Canvas Syllabus tab body) |
--chapters |
16 |
Number of Module N modules to generate |
--output |
./output |
Output directory |
--level |
advanced-undergrad |
Audience |
--length |
standard |
concise / standard / comprehensive |
--cohort |
60 |
Class size |
--notes |
— | Additional learner context |
--provider |
anthropic |
LLM provider — anthropic or ollama. With ollama, set OLLAMA_API_KEY; with anthropic, set ANTHROPIC_API_KEY |
--ollama-model |
gpt-oss:120b-cloud |
Ollama model used when --provider ollama |
--concurrency |
3 |
Parallel chapters during Canvas Module generation |
--syllabus |
— | Path to existing syllabus.json (skip regeneration) |
--skip-canvas-module |
false |
Skip per-chapter content generation (for fast manifest-only test runs) |
Ollama example:
OLLAMA_API_KEY=... npx tsx scripts/canvas-course.ts \
--provider ollama \
--ollama-model gpt-oss:120b-cloud \
--topic "Statistics for Data Science" \
--template ./template.imscc \
--chapters 8 \
--output ./output/statsIf both OLLAMA_API_KEY and ANTHROPIC_API_KEY are set, outline-DOCX extraction always uses Claude (its instruction-following on the small JSON-extraction task is more reliable) regardless of --provider.
Original ClassBuild CLI (scripts/generate-course.ts)
The upstream CLI is unchanged. It does not know about the Canvas template export, the outline DOCX path, or the Ollama backend — use it for the multimedia (slides + audio + infographic) flow.
src/
services/
template/
parser.ts # parse uploaded .imscc → Template
generateChapter.ts # per-chapter Canvas Module generation
templateImsccExporter.ts # export .imscc using uploaded template as base
parseOutlineDocx.ts # mammoth + LLM extraction
research/
anthropic.ts # Claude web_search backend
tavily.ts # Tavily REST + LLM synthesis
wikipedia.ts # Wikipedia API + LLM synthesis
llm/
anthropic.ts # Anthropic SDK streaming
ollama.ts # Ollama Cloud NDJSON streaming
store/
templateStore.ts # uploaded templates (parsed + raw .imscc Blob)
pages/
TemplatePreviewPage.tsx # /templates upload + preview
components/
setup/
TemplatePicker.tsx
CourseOutlineUpload.tsx
syllabus/
TemplateTitleEditor.tsx
React 19 · Vite 7 · TypeScript 5.9 · Tailwind CSS 4 · Zustand · JSZip · mammoth.js · Claude (Sonnet 4.6 / Opus 4.6 / Haiku 4.5) · Ollama Cloud · Tavily · Gemini
See CONTRIBUTING.md for dev setup, commit conventions, and what every PR needs. CI runs tsc -b, npm run lint, npm test, and npm run build on every push and PR.
Built on ClassBuild by Jason Tangen — the foundation of this fork. The IMSCC export, Canvas template handling, course-outline DOCX parsing, multi-provider LLM/research routing, and the rebrand are added on top, but the original learning-science engine, prompt library, and content generators are all from upstream.
MIT — same as upstream.