How to Embed Polypad Virtual Manipulatives in Your Web App 2025

Prerequisites and Setup Checklist

What You Need Before Starting

Before you drop a single line of iFrame code, confirm your environment covers these bases. Polypad is forgiving — it's free, requires no installation, and runs on virtually everything — but your hosting environment does have a few constraints worth checking upfront.

  • [ ] A modern browser (see matrix below) on your development machine
  • [ ] Node.js 18+ if you're working in a React or Next.js project
  • [ ] A valid HTTPS origin for your app in production (Polypad loads over HTTPS; mixed-content embeds will fail)
  • [ ] An Amplify account only if you plan to use teacher-assignment features; public embeds need nothing
  • [ ] Basic familiarity with iFrame attributes (src, allow, sandbox)
  • [ ] (Optional) Next.js 13+ App Router or Pages Router project already scaffolded

Estimated time: 25 minutes for a fully responsive, localized embed in a Next.js app.

Supported Browsers and Devices

| Browser | Desktop | Tablet | Mobile | Notes | |---|---|---|---|---| | Chrome 110+ | ✅ | ✅ | ✅ | Best experience; recommended for teachers | | Firefox 110+ | ✅ | ✅ | ✅ | Full support | | Safari 15.4+ | ✅ | ✅ | ⚠️ | Requires allow-scripts in sandbox — see Step 5 | | Edge 110+ | ✅ | ✅ | ✅ | Chromium-based; matches Chrome |

Polypad Account vs. No-Login Usage

Polypad is completely free to use and requires no login for public embeds. Students can interact with an embedded canvas — dragging fraction bars, spinning spinners, building polyhedra — without ever touching an account creation form. That's the default, zero-friction embed path this guide covers.

The assignment flow is different. When a teacher names an activity and clicks "Continue" in the Assigning dialog, Amplify provisions a real-time classroom session where the teacher can view each student's work live. That workflow requires a free Amplify teacher account. Students joining an assigned activity still don't need their own accounts in most configurations.


Step 1: Generate a Polypad Embed URL or iFrame Code

You need the actual iFrame snippet before writing any application code. Polypad's Share Options panel generates it for you in about 30 seconds, and understanding its output saves you from hand-crafting URLs that break.

Using the Share Options Panel in the Polypad UI

  1. Open polypad.amplify.com and build (or open) the canvas you want to embed.
  2. Click the Share button in the top toolbar.
  3. In the Share Options modal you'll see: Embed, Classroom, X (Twitter), Facebook, Pinterest, Reddit, and a Copy Link button.
  4. Click Embed. Polypad displays an iFrame snippet with your canvas URL pre-populated.

Choosing Between Public Link and Embeddable iFrame

"Copy Link" gives you a URL suitable for pasting into an LMS or a chat message — students click it and open Polypad directly. Embed gives you the <iframe> tag for inline rendering inside your own web page. For app integration, you always want the Embed path.

Configuring Embed Parameters (Size, Language, Dark Mode)

The Polypad UI exposes width/height fields in the Embed panel. Beyond those, you can manually append query parameters to the src URL:

  • ?lang=de for German, ?lang=fr for French, ?lang=ar for Arabic, etc.
  • ?theme=dark to force dark mode regardless of the user's OS preference.
<!-- Code Example 1: Basic Polypad iFrame embed -->
<iframe
  src="https://polypad.amplify.com/embed?lang=en-us"
  width="800"
  height="600"
  frameborder="0"
  allowfullscreen
  allow="clipboard-read; clipboard-write"
  title="Polypad Virtual Manipulatives"
></iframe>

Note: Replace the src with the specific canvas URL from the Share Options panel. The snippet above uses the generic embed endpoint; a saved canvas will have a unique path like https://polypad.amplify.com/p/abc123.


Step 2: Add the Embed to a React or Next.js Application

Dropping raw HTML into JSX is one thing; building a reusable component that handles responsive sizing, focus management, and iFrame security is another. This step gives you a production-ready component.

Creating a PolypadEmbed Component in React

// components/PolypadEmbed.jsx
import { useRef, useEffect } from 'react';

/**
 * PolypadEmbed — wraps a Polypad canvas in a responsive, accessible iFrame.
 *
 * Props:
 *   activityUrl  {string}  Full Polypad embed URL (from Share > Embed panel)
 *   width        {string}  CSS width value, e.g. "100%" or "800px" (default: "100%")
 *   height       {string}  CSS height value, e.g. "600px" (default: "600px")
 *   title        {string}  Accessible iFrame title (required for screen readers)
 */
export default function PolypadEmbed({
  activityUrl,
  width = '100%',
  height = '600px',
  title = 'Polypad Virtual Manipulatives',
}) {
  const iframeRef = useRef(null);

  // Optional: focus the iframe after mount so keyboard users
  // can interact without an extra click.
  useEffect(() => {
    if (iframeRef.current) {
      iframeRef.current.focus();
    }
  }, [activityUrl]);

  return (
    <div className="polypad-wrapper">
      <iframe
        ref={iframeRef}
        src={activityUrl}
        width={width}
        height={height}
        frameBorder="0"
        allowFullScreen
        allow="clipboard-read; clipboard-write"
        sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
        title={title}
        loading="lazy"
      />
    </div>
  );
}

Handling Responsive Layout with CSS

Fixed pixel heights break on phones. Use aspect-ratio to maintain a 4:3 canvas ratio regardless of viewport width:

/* styles/polypad.css */
.polypad-wrapper {
  width: 100%;
  aspect-ratio: 4 / 3;
  position: relative;
  overflow: hidden;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12);
}

.polypad-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
}

When using this CSS, pass width="100%" and height="100%" as props, and let the wrapper control the dimensions.

Avoiding Common iFrame Security Warnings (sandbox, allow)

The sandbox attribute is a whitelist. Without it, the iFrame inherits your page's full permissions. Without the right values, Polypad breaks silently. The four values you need:

| Sandbox value | Why Polypad needs it | |---|---| | allow-scripts | Runs Polypad's JavaScript engine | | allow-same-origin | Preserves localStorage for canvas state | | allow-popups | Opens help links and share dialogs | | allow-forms | Enables login/assignment forms inside the embed |

Note: Never add allow-same-origin alongside allow-scripts on an iFrame you don't control the content of — it bypasses sandboxing. Polypad is a trusted Amplify domain, so this combination is safe here.


Step 3: Assign a Polypad Activity Programmatically via Share Links

Teachers often need to push a specific Polypad canvas to a whole class without manually copying links. You can automate this by constructing named-activity URLs in code — useful if you're building an LMS integration or an admin dashboard.

Understanding the Amplify Assignment Flow

When a teacher clicks "Assign" in Polypad's UI, they give the activity a name, and Amplify creates a classroom session URL. Students join via that URL, and the teacher watches their canvases update live. The session URL encodes the activity name and the teacher's account context. You can partially replicate the link-generation step without the live session (which still requires a teacher account) by encoding an activity name in the shareable URL.

Constructing a Named Activity URL for Students

// utils/polypadUrl.js

const POLYPAD_BASE_URL = 'https://polypad.amplify.com';

/**
 * buildPolypadActivityUrl
 * Constructs a Polypad share URL with an encoded activity name.
 *
 * @param {string} activityName  Human-readable name, e.g. "Fraction Bars Week 3"
 * @param {string} [canvasPath]  Optional saved-canvas path, e.g. "/p/abc123"
 * @param {string} [lang]        Optional language code, e.g. "es"
 * @returns {string}             Full Polypad URL ready to share with students
 */
export function buildPolypadActivityUrl(activityName, canvasPath = '', lang = 'en-us') {
  const params = new URLSearchParams({
    activity: activityName,
    lang,
  });

  const path = canvasPath ? canvasPath : '/embed';
  return `${POLYPAD_BASE_URL}${path}?${params.toString()}`;
}

// --- Usage example ---
const studentLink = buildPolypadActivityUrl(
  'Fraction Bars Week 3',
  '/p/abc123',
  'en-us'
);

console.log(studentLink);
// https://polypad.amplify.com/p/abc123?activity=Fraction+Bars+Week+3&lang=en-us

// Open in a new tab:
window.open(studentLink, '_blank', 'noopener,noreferrer');

// Or populate an anchor tag dynamically:
const anchor = document.getElementById('student-link');
anchor.href = studentLink;
anchor.textContent = 'Open Your Fraction Bars Activity';

Passing a Pre-Named Activity Link via URL Parameters

This utility is especially useful in an LMS where you iterate over a roster and generate per-student or per-class links. Combine it with your database of saved canvas paths to produce a full assignment dashboard without leaving your own app.

Note: The live "view student work in real time" feature requires the teacher to be logged into an Amplify account and the session to be created through Amplify's assignment flow. Programmatic URL construction gets students to the right canvas; it doesn't replicate the real-time monitoring layer.


Step 4: Localize Polypad for International Classrooms

Polypad ships with 22+ language options baked in — a rare capability among math tools. If your platform serves multilingual students, injecting the right lang parameter automatically spares students from manually switching languages inside the embed.

Supported Language Codes (Arabic, Chinese, Hindi, and 18 More)

| Language | Polypad code | BCP-47 locale | RTL? | |---|---|---|---| | English (US) | en-us | en-US | No | | Arabic | ar | ar | Yes | | Chinese | zh | zh-CN | No | | German | de | de-DE | No | | Spanish | es | es-ES | No | | Estonian | et | et-EE | No | | Farsi/Persian | fa | fa-IR | Yes | | French | fr | fr-FR | No | | Hebrew | he | he-IL | Yes | | Hindi | hi | hi-IN | No | | Croatian | hr | hr-HR | No | | Hungarian | hu | hu-HU | No | | Indonesian | id | id-ID | No | | Italian | it | it-IT | No | | Japanese | ja | ja-JP | No | | Korean | ko | ko-KR | No | | Dutch | nl | nl-NL | No | | Polish | pl | pl-PL | No | | Portuguese | pt | pt-BR | No | | Romanian | ro | ro-RO | No | | Russian | ru | ru-RU | No | | Swedish | sv | sv-SE | No | | Thai | th | th-TH | No | | Turkish | tr | tr-TR | No | | Ukrainian | uk | uk-UA | No | | Vietnamese | vi | vi-VN | No |

Injecting a Language Parameter into the Embed URL

// pages/math-lab.jsx  (Next.js Pages Router)
import { useRouter } from 'next/router';
import PolypadEmbed from '../components/PolypadEmbed';

// Map Next.js locale codes to Polypad language codes
const LOCALE_TO_POLYPAD = {
  'en-US': 'en-us',
  'en':    'en-us',
  'ar':    'ar',
  'zh':    'zh',
  'zh-CN': 'zh',
  'de':    'de',
  'es':    'es',
  'fa':    'fa',
  'fr':    'fr',
  'he':    'he',
  'hi':    'hi',
  'hr':    'hr',
  'hu':    'hu',
  'id':    'id',
  'it':    'it',
  'ja':    'ja',
  'ko':    'ko',
  'nl':    'nl',
  'pl':    'pl',
  'pt':    'pt',
  'ro':    'ro',
  'ru':    'ru',
  'sv':    'sv',
  'th':    'th',
  'tr':    'tr',
  'uk':    'uk',
  'vi':    'vi',
};

const CANVAS_PATH = '/p/abc123'; // Replace with your saved canvas path
const POLYPAD_BASE = 'https://polypad.amplify.com';

export default function MathLabPage() {
  const { locale } = useRouter();

  const polypadLang = LOCALE_TO_POLYPAD[locale] ?? 'en-us';
  const isRTL = ['ar', 'fa', 'he'].includes(polypadLang);

  const activityUrl = `${POLYPAD_BASE}${CANVAS_PATH}?lang=${polypadLang}`;

  return (
    <main dir={isRTL ? 'rtl' : 'ltr'}>
      <h1>Math Lab</h1>
      <PolypadEmbed
        activityUrl={activityUrl}
        title={`Polypad – ${locale}`}
      />
    </main>
  );
}

RTL Language Considerations (Arabic, Hebrew, Farsi)

Polypad handles internal RTL rendering automatically when you set ?lang=ar, ?lang=he, or ?lang=fa. What it cannot control is your parent container layout. Set dir="rtl" on the wrapping element (as shown above) so that any sibling UI — instructions, buttons, labels — mirrors correctly. Also check that your CSS doesn't hard-code margin-left or text-align: left in components adjacent to the embed.


Step 5: Embed Specific Manipulative Types for Targeted Lessons

Polypad's real power isn't just an open sandbox — it's the ability to curate a canvas pre-loaded with exactly the tools a lesson needs, and then lock students into that focused environment. This is the "Endlessly customisable" feature the Polypad team highlights.

Fraction Bars, Balance Scale, and Function Machines

For a fractions unit, you don't want students accidentally dragging in polyhedra or logic gates. Here's the workflow:

  1. Open a fresh canvas on polypad.amplify.com.
  2. From the left toolbar, add only Fraction Bars tiles to the canvas.
  3. Arrange them in the starting position you want students to see.
  4. Click Share > Copy Link (or Embed) — the URL now encodes your canvas state.
  5. Use that URL as the activityUrl prop in Code Example 1 or 2.

Students who open this link land on your pre-configured canvas. They can still access other tools from the toolbar unless you've hidden them — curating the starting state is the key lever here.

Dice, Coins, Spinners for Probability Modules

The same pattern works for probability: place a set of dice, a coin, and a custom spinner on the canvas, set initial values, save, and share. Polypad's dice and spinners are interactive — students click to roll or spin — making them ideal for simulation-based lessons without writing a single line of probability code yourself.

3D Polyhedra and Logic Gates for Advanced Topics

For geometry or CS electives, pre-load a dodecahedron or a logic gate circuit. Polypad renders 3D polyhedra with real-time rotation, and logic gates respond to binary inputs — sophisticated tools that would take weeks to build in-house. Grab the embed URL from a saved canvas and inject it using Code Example 1.

Note: The embed URL from the Share panel encodes the full canvas state as a path segment or hash. It is not a separately documented API — treat it as opaque and regenerate it from the UI whenever you change the canvas layout significantly.


Common Issues and Fixes

Error: iFrame Blocked by Content Security Policy (CSP) Headers

Cause: Your server sends a Content-Security-Policy header with a frame-src or default-src directive that doesn't include polypad.amplify.com, so the browser refuses to load the iFrame.

Fix — Express.js middleware:

// middleware/csp.js
app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    [
      "default-src 'self'",
      "script-src 'self' 'unsafe-inline'",   // tighten as needed
      "frame-src 'self' https://polypad.amplify.com",
      "frame-ancestors 'self'",
    ].join('; ')
  );
  next();
});

Fix — Next.js next.config.js:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: [
              "default-src 'self'",
              "script-src 'self' 'unsafe-inline' 'unsafe-eval'",
              "frame-src 'self' https://polypad.amplify.com",
            ].join('; '),
          },
        ],
      },
    ];
  },
};

module.exports = nextConfig;

Note: Scope the frame-src to https://polypad.amplify.com specifically rather than using a wildcard. This avoids opening your CSP to unintended embedding origins.

Error: Polypad Shows Blank Screen — JavaScript Not Enabled

Cause: The Polypad application is a JavaScript-heavy SPA. If the browser has JS disabled (rare, but happens in locked-down school environments), the iFrame renders a blank white box.

Fix: Add a <noscript> fallback visible to the user and confirm JS is active in the host environment before rendering the component:

<noscript>
  <p>Please enable JavaScript in your browser to access Polypad.
     <a href="https://polypad.amplify.com" target="_blank" rel="noopener">
       Open Polypad in a new tab instead.
     </a>
  </p>
</noscript>

For managed Chromebook or MDM environments, verify that polypad.amplify.com is on the allowlist for JavaScript execution.

Error: Embed Not Loading on iOS Safari — allow-scripts Missing

Cause: iOS Safari enforces sandbox attributes more strictly than desktop browsers. If you add sandbox without allow-scripts, all JavaScript is blocked and Polypad renders nothing.

Fix: Ensure your sandbox attribute includes at minimum allow-scripts allow-same-origin:

<iframe
  src="https://polypad.amplify.com/embed"
  sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
  allowfullscreen
  title="Polypad"
></iframe>

Also confirm you're serving your page over HTTPS — iOS Safari blocks mixed-content iFrames outright on HTTP origins.

Error: Dark Mode Not Applying Inside the Embed

Cause: The ?theme=dark parameter must be appended to the src URL. Setting prefers-color-scheme: dark in your CSS has no effect inside the iFrame.

Fix: Detect the user's preference in JavaScript and append the parameter:

const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = prefersDark ? 'dark' : 'light';
const src = `https://polypad.amplify.com/embed?lang=en-us&theme=${theme}`;

Pass src as the activityUrl prop to PolypadEmbed.


FAQ

Q: Does embedding Polypad require users to create an Amplify account?

No — public embeds work entirely without a login. Students can interact with any embedded Polypad canvas anonymously. An Amplify account is only required for teachers who want to use the assignment flow (naming activities, viewing student work in real time, accessing classroom analytics). Even then, students joining an assigned session typically don't need their own accounts.

Q: Can I embed Polypad in a mobile React Native WebView?

Yes, with caveats. Use the react-native-webview package and set javaScriptEnabled={true} and domStorageEnabled={true} on the WebView component — both are required for Polypad's SPA to boot. The sandbox attribute is not supported in React Native's WebView in the same way as HTML iFrames, so you lose some CSP granularity. Test thoroughly on iOS, since Safari's engine (WKWebView, which React Native uses on iOS) has historically been the strictest about localStorage and cross-origin behavior.

Q: Is Polypad free to embed for commercial educational platforms?

Polypad is described as "completely free to use" with no login or installation required. For commercial use in a paid educational platform, you should review Amplify's current Terms of Service at amplify.com to confirm that commercial embedding is permitted and whether attribution or partnership agreements are required. The free-to-use policy has been consistent since Polypad's Mathigon origins, but enterprise integrations at scale may warrant a conversation with Amplify's partnerships team.