Add MPDF to your app
MPDF carries music information in standard PDF attachments. Implement the base format separately from optional extensions to add playback while retaining your existing PDF rendering.
The base format: mpdf.json
Read mpdf.json from the PDF EmbeddedFiles name tree. The MPDF 1.x base format describes tracks, page cues, and playback options. Embedded audio uses separate PDF attachments referenced with file:.
{
"mpdf": "1.0",
"title": "Practice score",
"tracks": [
{ "id": "backing", "title": "Backing track", "src": "file:backing.m4a", "start": 10, "end": 70 }
],
"cues": [ { "page": 1, "track": "backing", "at": 10 } ],
"options": { "autoplay": false, "followPage": false, "loop": false }
}Pages are one-based. start and end are seconds in the original recording; cue at uses the same absolute source-recording clock. In this example, at: 10 means second 10 in the original recording. Optional timeMarkers.seconds is relative to the track's start.
Optional extensions: read and preserve
| Data | Purpose |
|---|---|
buttons | On-page playback button positions and track references |
timeMarkers | Page positions and relative times within tracks |
seetogether-mpdf.json | SEETO production metadata, playback choices, creator claims, and sharing state |
seetogether-score.json | Included Copy Note score and chord playback data |
| Editable source and markup attachments | Creator revision material, excluded from public sharing copies |
These extensions are not required for base MPDF playback. Ignore unsupported fields when reading, but preserve them when making revisions. To download an unchanged original, return its original bytes without rewriting the PDF. A sharing copy is a separate, explicit export retaining only the data intended for recipients.
Read and play
const pdf = await pdfjsLib.getDocument({ data: bytes.slice() }).promise
const attachments = (await pdf.getAttachments()) || {}
const raw = attachments['mpdf.json']
if (raw) {
const manifest = JSON.parse(new TextDecoder().decode(raw.content))
// Validate version, size limits, unique IDs, page ranges and track references.
// Render text with textContent, never as HTML. Keep unknown extension fields.
// Resolve file: names against attachments; never treat them as filesystem paths.
// Retain `bytes` for an unchanged download; rewriting changes the file hash.
}- Use a visible official player for
youtube:, the matching PDF attachment forfile:, and a valid external audio URL forhttps:. External playback depends on the server's CORS, format, and access settings. - Do not autoplay before a user gesture. Respect the reader's page-following setting. A page without a cue leaves the current track playing.
- Display buttons and markers as a separate interaction layer, without rewriting the PDF page. Keep the player collapsible or outside the score area.
- Preserve Unicode filenames, but reject paths and reserved metadata names as audio. Validate sizes, page ranges, times, IDs, and references.
Create a new MPDF
from pypdf import PdfWriter
import json
# Create a NEW MPDF from a plain PDF; do not overwrite the input.
w = PdfWriter(clone_from="score.pdf")
w.add_attachment("mpdf.json", json.dumps(manifest).encode("utf-8"))
w.add_attachment("backing.m4a", audio_bytes)
w.write("score-with-music.pdf")This is a minimal example for producing a new file from a plain PDF. Revising existing MPDFs also requires handling name trees, unknown attachments, and extensions. The repository's JavaScript and Python base references are distinct from the website's compatibility layer in site/mpdf-browser.js.
A creator claim is not proof of ownership
SEETO creator revisions use a non-anonymous account and SHA-256 of the entire file. The immutable registry's file hash, publication ID, and owner UID must all match. Embedded creatorUID alone must never unlock editing. sharedReadOnly: true remains read-only for the creator as well. Register the finalized file before offering its registered creator original.
Public links are a separate service
This website's Cloudflare Pages Functions store sharing PDFs. They reject embedded audio, editable sources, and unsupported attachments, and stop serving links 90 days after creation. This hosting policy and SEETO account integration are not mandatory parts of the MPDF base format.
Code is MIT licensed; specification text is CC BY 4.0. Use GitHub Issues for technical proposals and support email for private material or removal requests.