Why is my SCORM package too large?
A SCORM package is too large when the zip exceeds the upload limit your LMS or its web server enforces. SCORM itself sets no size cap. Almost always the weight is media rather than code: video, uncompressed audio, and full-resolution images. Shrink those three and the package fits without changing a single line of the course.
The limit you hit is rarely a SCORM rule. It is the hosting stack: a maximum upload size in the web server configuration, a matching limit in PHP or the application runtime, a request timeout that fires before a slow upload finishes, or a per-course quota an administrator set. Those numbers vary by platform and by installation, which is why the same package uploads to one LMS and is rejected by another.
Size costs you twice. The first cost is the upload, which you notice. The second is every learner, which you do not: the browser downloads those assets over whatever connection the learner has, so a heavy package means a slow first page, stalled video, and learners closing the tab before the runtime has committed anything. A package that is small for the LMS is also faster for the person taking it.
Find what is heavy
Do not guess, and do not start by re-zipping. Unzip the package and measure it, because the fix for 400 MB of video is nothing like the fix for 4,000 tiny files.
unzip -l course.zip | sort -k1 -rn | head -20 # biggest entries, uncompressed unzip -q course.zip -d course/ du -sh course/* # where the weight sits by folder find course/ -type f -printf '%s\t%p\n' | sort -rn | head -20
Two minutes of that usually ends the investigation. The distribution is almost always lopsided: one or two media files account for most of the package, and everything else is noise.
| What you find | Typical cause | What actually works |
|---|---|---|
| One or two very large .mp4 files | Exported at source resolution and bitrate | Re-encode, or externalise and stream |
| Many .wav or high-bitrate .mp3 files | Narration exported uncompressed, per slide | Re-encode to mono at a speech bitrate |
| Large .png screenshots and photos | PNG used for everything by default | Resize to display size; JPEG for photos |
| A big fonts or lib folder | Whole icon and font families shipped | Subset to the weights you actually use |
| Source files: .pptx, .psd, .ai, .mov | Authoring tool exported its working files | Remove — they are never served |
One thing that will not work: compressing harder. MP4, MP3, JPEG and PNG are already compressed, so a package that is mostly media gains a few percent from a better zip setting and no more. The size has to come out of the assets themselves.
Compress and re-encode media
Re-encoding is where the large wins are, and for most courses it is invisible to the learner.
- Video. H.264 in an MP4 container plays everywhere. Screen recordings and talking heads rarely need 1080p — 720p is usually indistinguishable at the size the player actually renders. Encode with a quality target rather than a fixed bitrate so simple footage gets smaller automatically, and cap the frame rate at 30 unless the content is genuinely motion-heavy.
- Audio. Narration is speech, and speech is mono. Exporting voice-over as stereo doubles the data for no audible gain; a modest mono bitrate is transparent for a human voice and a fraction of the size of the WAV it came from.
- Images. Resize to the pixel dimensions the layout displays, then choose format by content: JPEG for photographs and screenshots, PNG only where you need flat colour or transparency. A 4,000-pixel-wide photo shown in a 800-pixel column is carrying five times the data it needs.
- Fonts. Ship the weights the course uses, not the family. Subsetting a font to the characters and weights present in the content routinely removes hundreds of kilobytes.
Re-encode from the highest-quality source you still have, not from the file already in the package — compressing an already-compressed file twice produces visible artefacts for very little extra saving. Then replace the asset in place, keeping the same filename, so nothing in the HTML or the manifest has to change.
Externalise large video
When one video is most of the package, the fastest fix is to stop shipping it. Leave the SCO in the zip and point the player at a URL instead of a bundled file, so the package carries the course and the network carries the media.
<!-- was -->
<video src="media/module-01.mp4"></video>
<!-- now -->
<video src="https://media.example.org/courses/module-01.mp4"
preload="metadata" playsinline></video>
Tracking is unaffected. The runtime conversation happens between the SCO and the LMS in JavaScript; it does not care where a video file came from. Status, score, bookmark and commit behave exactly as before.
The trade is self-containment. A package with bundled media is one artefact that works anywhere; a package with external media depends on a URL staying alive for the life of the course. Version the media path rather than overwriting files at the same URL, so an updated video never silently appears inside an old course. Converting video to SCORM covers the packaging side of the same decision.
Strip unused assets
Authoring tools export generously. It is common to find unused themes and templates, locale files for languages the course does not offer, several copies of the same JavaScript library, and the original source documents sitting beside their published output. None of it is served and all of it is in the zip.
Find what is genuinely referenced before deleting anything:
grep -rlio 'bigfile\.png' course/ --include=*.html --include=*.css --include=*.js grep -rhoE '(src|href)="[^"]+"' course/ --include=*.html | sort -u
imsmanifest.xml is supposed to declare every file a resource needs, but plenty of authoring tools list only the launch page and leave images, stylesheets and scripts undeclared. A file missing from the manifest is not proof it is unused — it may be referenced from CSS, injected by script, or loaded by a name the exporter built at runtime. Search the content, keep anything you cannot account for, and re-zip from a copy so the original is always recoverable.When you re-zip, build the archive from inside the course folder so imsmanifest.xml sits at the root of the zip and not inside a wrapper directory. A manifest one level down is the single most common reason a freshly rebuilt package is rejected on import. Then run the shrunk package before it goes anywhere near a cohort — testing SCORM before upload walks the checks that catch a stripped file the course still wanted.
When to host instead of embed
If you are trimming the same course every few months, the size is a symptom. At that point the question is not how to compress harder but whether the content should live in the package at all.
The alternative is a thin launcher: a small package that contains the manifest and a launch page, with the course itself hosted elsewhere and loaded at run time. The zip becomes trivial in size, and updating the course no longer means re-importing a zip into every LMS that has a copy. Dispatch explained covers how that launcher reports back to the LMS.
It is not a free win. Hosting means you now run something: a server, a domain, TLS certificates, and an availability problem that belongs to you rather than to the LMS vendor. Learners on restricted networks may be unable to reach it. And the LMS records only what the launcher reports, so a broken connection is a lost attempt rather than a slow one.
- Compress when one export was careless and the course is otherwise fine — the common case, and the cheapest.
- Externalise media when a single course is dominated by video and your learners are reliably online.
- Host the course when the content changes often, is distributed to several LMSs, or is large in ways that compression cannot reach.
- Split into modules when none of the above helps: several smaller packages import where one large one will not, and learners resume more reliably in shorter sessions.
Where SCORM Central fits
SCORM Central converts files you already have — PDF, PowerPoint, Word, or video — into SCORM 1.2, SCORM 2004, cmi5, or xAPI, and shows you the manifest, the full runtime log, and a conformance report on every build, so you can see exactly what a package will report before an LMS does. See how it works, or start with one file.