Embed QuizBase trivia in Moodle and Canvas LMS#
Moodle and Canvas both let you paste raw HTML into a Page activity. That’s all the surface area you need to drop in a working five-question trivia activity that pulls from QuizBase, runs on the student’s device, and reports the score back to the student (or, in a paid Canvas tier, back to the gradebook). No plugin install, no admin permission, no LTI integration headache.
This guide gives you a single <script>+<div> snippet you paste into a Moodle Page or Canvas Page editor. Five questions, four-choice each, score reveal at the end, attribution rendered on every card. About 60 lines of self-contained HTML.
What you’ll build#
A self-contained HTML widget that:
- Renders inside a Moodle Page or Canvas Page — works in both editors
- Fetches 5 questions from QuizBase on load (one round, no pagination)
- Shows one question at a time with 4 shuffled answer choices
- Tracks correct/incorrect locally in browser memory (resets on page refresh)
- Shows final score at the end + “try again” button
- Renders attribution on every question card — license compliance baked in
- Configurable category and difficulty via inline HTML data attributes
The mechanic — single-page batch fetch with stable IDs#
Five questions fetched in one paginated call via /api/v1/questions. We use ?limit=5 and pass category + lang as query params. Each question has a stable id we don’t need — the student doesn’t replay individual cards, the quiz is one-shot. But the IDs make it possible to add features later (skip-this-one-later, report-wrong, link from gradebook). See /docs/api/questions-by-id for the stable-ID pattern catalogue.
Stack#
- A QuizBase publishable key —
qb_pk_*, free tier. Note: the key is visible to the student in this embed model (it’s in the inline<script>). For a public-school Moodle that’s fine — publishable keys are read-only and the free tier doesn’t bill on usage. For internal-only Canvas, see “Pitfalls” below. - A Moodle or Canvas instance where you can create Page activities — every standard install supports this.
Step 1 — Get a QuizBase publishable key#
Create a qb_pk_* key. Publishable is the right kind here — read-only API access, safe to embed in HTML the student can View Source on. Free tier 500 req/day = ~100 classes of 30 students taking the same quiz; for bigger schools or repeat usage, upgrade.
Sign up if you don’t have an account — no credit card, full production data on free tier.
Step 2 — Build the embed snippet#
The whole widget is 60 lines of HTML + inline JS. Save this to a text file, replace YOUR_KEY and CATEGORY:
<div id="quizbase-widget" data-category="science-and-nature" data-difficulty="medium" data-lang="en">
<p>Loading...</p>
</div>
<script>
(async function () {
const KEY = 'YOUR_PUBLISHABLE_KEY'; // qb_pk_*
const el = document.getElementById('quizbase-widget');
const cat = el.dataset.category;
const diff = el.dataset.difficulty;
const lang = el.dataset.lang;
const url = `https://quizbase.runriva.com/api/v1/questions/random?category=${cat}&difficulty=${diff}&lang=${lang}&amount=5`;
const r = await fetch(url, { headers: { 'X-API-Key': KEY } });
const { data: questions } = await r.json();
let index = 0;
let score = 0;
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function render() {
if (index >= questions.length) {
el.innerHTML = `<h3>Score: ${score} / ${questions.length}</h3>
<button onclick="location.reload()">Try again</button>`;
return;
}
const q = questions[index];
const choices = shuffle([q.correctAnswer, ...q.incorrectAnswers]);
el.innerHTML = `<h3>Q${index + 1}: ${q.text}</h3>
<div>${choices.map((c, i) => `<button onclick="window.__pick('${c.replace(/'/g, "\'")}')">${'ABCD'[i]}. ${c}</button>`).join('<br>')}</div>
<small>Source: ${q.attribution.author} (${q.attribution.license})</small>`;
}
window.__pick = function (choice) {
const q = questions[index];
if (choice === q.correctAnswer) score++;
index++;
render();
};
render();
})();
</script>
<img
src="/docs/guides/moodle-canvas-lms-module-hero.webp"
alt="moodle-canvas-lms-module hero illustration"
class="my-6 aspect-video w-full rounded-2xl object-cover shadow-sm"
loading="lazy"
/>
<style>
#quizbase-widget button {
display: block;
margin: 4px 0;
padding: 8px 12px;
text-align: left;
min-width: 240px;
}
#quizbase-widget small {
display: block;
margin-top: 12px;
color: #888;
font-size: 12px;
}
</style> The <small> attribution at the bottom of each question card is required. QuizBase questions ship under CC BY-SA / CC BY / MIT, and surfacing source on every card is a license requirement. See /data for the full attribution rules.
Step 3 — Paste into Moodle Page#
In your Moodle course: Add an activity or resource → Page. Give it a title, then in the Page content editor switch to the HTML source view (the </> icon in the toolbar). Paste the entire snippet from Step 2. Save. Students opening the page now see a working quiz.
To change category, edit the HTML and update the data-category attribute — slug values come from /api/v1/categories (science-and-nature, general-knowledge, history, etc.).
Step 4 — Paste into Canvas Page#
Canvas: Pages → New Page. Give it a title. In the rich editor click the </> icon (HTML editor toggle). Paste the snippet. Save. Same result — students see a working quiz when they open the page.
Canvas allows iframe embed too if your school has stricter HTML sanitization. In that case host the snippet on a public URL (GitHub Pages, Netlify, your school’s WordPress) and embed via <iframe src="..."> — Canvas allows iframes from the same domain plus a school-managed allow list.
Step 5 — Optional: category/difficulty picker for teachers#
Make the quiz reusable across classes — add a small <select> at the top so teachers pick category/difficulty without re-editing HTML each time:
<div id="quizbase-controls">
Category:
<select id="cat">
<option value="science-and-nature">Science</option>
<option value="history">History</option>
<option value="geography">Geography</option>
<option value="animals">Animals</option>
</select>
Difficulty:
<select id="diff">
<option value="easy">Easy</option>
<option value="medium" selected>Medium</option>
<option value="hard">Hard</option>
</select>
<button onclick="window.__startQuiz()">Start</button>
</div>
<div id="quizbase-widget"></div> Wrap your existing fetch logic in window.__startQuiz = async () => { ... } and read selections from the dropdowns. Teachers now reuse the same Page across units without editing HTML.
Pitfalls#
- The publishable key is visible to students — they can View Source. For public-school Moodle this is fine (the key is read-only, free tier caps at 500 req/day). For internal-corporate or paid Canvas where the key value matters, proxy fetches through a tiny
/api/quizbaseendpoint on your school’s server. - Iframe embeds may have CORS issues — QuizBase REST has CORS open for
GETrequests. If you see CORS errors in DevTools, you’re calling from a Page that’s been sanitized into an iframe — check the iframe’ssrcdomain. - Moodle filters can strip
<script>— older Moodle 3.x sites had aggressive HTML cleaners. If your<script>tag disappears after save, ask your admin to enable the “Format HTML allows JavaScript” filter or upgrade to Moodle 4.0+. - Free tier rate limit shared across all students hitting the page in the same hour — 30 students × 5 questions × 6 classes/day = 900 req/day, above 500 free. Bigger schools should upgrade or proxy through cached responses.
What next#
- Multilingual Moodle pages — duplicate the Page per language, change the
data-langattribute. See languages and translations for the EN/PL/ES/FR matrix. - Anki shared-deck export — same content, different format. See Anki flashcards generator.
- LTI 1.3 gradebook sync — the big-school path. Requires hosting your own LTI provider (Moodle and Canvas both support 1.3). QuizBase as the question backend is unchanged.
- Bell-ringer warm-up variant — single-question card at the start of class. Replace
?limit=5with?limit=1, drop the score reveal, link from your Moodle course front page.
Ready to embed? Grab a free publishable key (no card, 500 req/day), paste the snippet into a Moodle or Canvas Page, ship to your class today. Bug in a question? Use POST /api/v1/report — or just edit the Page to skip it.