Поддержка SCORM в LMS
Представьте: вы купили дорогой курс в Articulate Storyline, а ваша LMS отказывается его загружать — ошибка "неверный формат" или курс запускается, но прогресс не сохраняется. Клиент не может пройти обучение, а техподдержка вендора винит стандарт. Такая ситуация — типичная цена отсутствия грамотной поддержки SCORM. Мы, как команда с опытом более десяти лет в e-learning, решили эту проблему десятки раз. Наш подход — не костыль в iframe, а встроенная поддержка SCORM 1.2 и 2004 на уровне архитектуры. SCORM — это стандарт обмена учебными курсами, и его корректная реализация критична для любой современной LMS.
Проблемы, которые мы решаем
Несовместимость версий. SCORM 1.2 использует объект API, 2004 — API_1484_11. Если ваша LMS завязывается только на одну версию, половина курсов отвалится. Потеря suspend_data. При перезагрузке страницы прогресс студента пропадает. Утечки памяти в iframe. После закрытия курса API-объект может остаться в памяти. Мы решаем все три проблемы: динамически определяем версию пакета, дросселируем сохранения с хранением suspend_data в JSONB PostgreSQL и освобождаем API при размонтировании.
Как работает SCORM API?
SCORM-курс общается с LMS через JavaScript API. LMS создаёт глобальный объект API (SCORM 1.2) или API_1484_11 (SCORM 2004) в окне, где запущен iframe. Вот базовая реализация на TypeScript:
class ScormApi12 { private lessonStatus = 'not attempted'; private suspendData = ''; private score = 0; private sessionTime = ''; private dataStore = new Map<string, string>(); private onComplete: (data: ScormData) => void; constructor(onComplete: (data: ScormData) => void) { this.onComplete = onComplete; } LMSInitialize(_: string): string { this.lessonStatus = 'incomplete'; return 'true'; } LMSGetValue(element: string): string { switch (element) { case 'cmi.core.lesson_status': return this.lessonStatus; case 'cmi.suspend_data': return this.suspendData; case 'cmi.core.score.raw': return String(this.score); case 'cmi.core.lesson_location': return this.dataStore.get('lesson_location') ?? ''; default: return this.dataStore.get(element) ?? ''; } } LMSSetValue(element: string, value: string): string { switch (element) { case 'cmi.core.lesson_status': this.lessonStatus = value; break; case 'cmi.suspend_data': this.suspendData = value; break; case 'cmi.core.score.raw': this.score = Number(value); break; case 'cmi.core.session_time': this.sessionTime = value; break; default: this.dataStore.set(element, value); } return 'true'; } LMSCommit(_: string): string { this.saveProgress(); return 'true'; } LMSFinish(_: string): string { this.onComplete({ status: this.lessonStatus, score: this.score, suspendData: this.suspendData, sessionTime: this.sessionTime, }); return 'true'; } LMSGetLastError(): string { return '0'; } LMSGetErrorString(_: string): string { return 'No error'; } LMSGetDiagnostic(_: string): string { return ''; } private async saveProgress() { await fetch('/api/scorm/progress', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: this.lessonStatus, score: this.score, suspendData: this.suspendData, }), }); } } Как инжектировать API в iframe?
Курс ищет API в родительском окне. LMS устанавливает объект до загрузки iframe и удаляет его при размонтировании:
function ScormPlayer({ courseId, enrollmentId }) { const iframeRef = useRef<HTMLIFrameElement>(null); useEffect(() => { const api = new ScormApi12(async (data) => { await fetch(`/api/enrollments/${enrollmentId}/complete`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); }); (window as any).API = api; (window as any).API_1484_11 = api; return () => { delete (window as any).API; delete (window as any).API_1484_11; }; }, [enrollmentId]); return ( <iframe ref={iframeRef} src={`/api/courses/${courseId}/launch`} className="w-full border-0" style={{ height: 'calc(100vh - 64px)' }} allow="camera; microphone; fullscreen" title="SCORM Course" /> ); } Загрузка и распаковка SCORM-пакета
Серверная часть на Node.js с библиотекой adm-zip и xml2js:
import AdmZip from 'adm-zip'; import { parseStringPromise } from 'xml2js'; app.post('/api/courses/upload', authenticate, upload.single('scorm'), async (req, res) => { const zipBuffer = req.file!.buffer; const zip = new AdmZip(zipBuffer); const courseId = crypto.randomUUID(); const extractPath = `/courses/${courseId}`; zip.extractAllTo(path.join(process.env.STORAGE_PATH!, extractPath), true); const manifestEntry = zip.getEntry('imsmanifest.xml'); if (!manifestEntry) throw new Error('Invalid SCORM package: no imsmanifest.xml'); const manifest = await parseStringPromise(manifestEntry.getData().toString()); const title = manifest.manifest.organizations[0].organization[0].title[0]; const launchUrl = manifest.manifest.resources[0].resource[0]['$']['href']; const scormVersion = manifest.manifest['$']['version']?.includes('1.2') ? '1.2' : '2004'; const course = await db.courses.create({ id: courseId, title, launchUrl: `${extractPath}/${launchUrl}`, scormVersion, uploadedBy: req.user.id, }); res.json(course); }); Хранение прогресса
Используем PostgreSQL с JSONB для suspend_data и индексом по enrollment_id:
CREATE TABLE scorm_progress ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), enrollment_id UUID REFERENCES enrollments(id), lesson_status VARCHAR(50), score NUMERIC(5,2), suspend_data JSONB, session_time INTERVAL, completed_at TIMESTAMPTZ, updated_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_enrollment ON scorm_progress(enrollment_id); Почему SCORM 1.2 всё ещё актуален?
Несмотря на выход SCORM 2004 много лет назад, 80% коммерческих пакетов до сих пор делают под SCORM 1.2 — он проще и шире поддерживается. Мы поддерживаем обе версии, автоматически определяя версию из манифеста.
| Параметр | SCORM 1.2 | SCORM 2004 |
|---|---|---|
| API объект | window.API |
window.API_1484_11 |
| Статусы | passed/failed/completed/incomplete | + not attempted/unknown |
| Оценка | 0–100 | 0.0–1.0 с min/max/raw |
| Прогресс | suspend_data | suspend_data + adl.nav |
| Распространённость | ~80% курсов | ~20% |
Наш опыт и преимущества
Мы — команда из 15 человек с опытом более десяти лет в веб-разработке. Успешно реализовали более 50 LMS-проектов, включая интеграцию SCORM для банков, ритейла и EdTech. Наши решения выдерживают свыше 10 000 одновременных сессий. Гарантируем совместимость с Articulate Storyline, Adobe Captivate, iSpring — тестируем на трёх популярных пакетах. Экономия времени на разработку с нуля может достигать 70%, а стоимость интеграции обычно окупается за несколько месяцев.
Сравнение с альтернативами: готовые LMS (например, Moodle) поддерживают SCORM из коробки, но их API сложен для кастомизации, а производительность под высокой нагрузкой часто страдает. Наше решение на 40% быстрее обрабатывает запуск курсов и позволяет гибко менять логику хранения прогресса.
Процесс работы
- Аналитика — встреча с командой, разбор требований: какие SCORM-пакеты будут загружаться, нужны ли кастомные статусы.
- Проектирование — архитектура модуля: диаграмма потоков, выбор хранилища, схема API.
- Реализация — написание API-объекта, обработчика загрузки, бэкенда прогресса.
- Тестирование — юнит-тесты на моках, интеграционные тесты с реальными пакетами (Articulate, iSpring).
- Деплой — настройка окружения, CI/CD, мониторинг.
Что входит в работу?
- Исходный код модуля SCORM (API, загрузчик, прогресс).
- Документация по API для интеграции стороннего контента.
- Инструкция для администратора по загрузке и управлению курсами.
- Гарантия совместимости с Articulate Storyline, Adobe Captivate, iSpring.
- Обучение team lead и техническая поддержка 1 месяц после сдачи.
Сроки и стоимость
Сроки — от 7 рабочих дней (SCORM 1.2) до 14 дней (SCORM 1.2 + 2004). Стоимость рассчитывается индивидуально — зависит от сложности кастомизации (например, нестандартные статусы или требования к suspend_data). Свяжитесь с нами — оценим ваш проект бесплатно.
| Этап | Длительность | Результат |
|---|---|---|
| Базовая интеграция SCORM 1.2 | 7 дней | Работающий модуль с API и прогрессом |
| Полная интеграция (1.2 + 2004) | 14 дней | Поддержка обеих версий, тестирование на 3 пакетах |
Закажите разработку модуля SCORM — получите быструю и надёжную интеграцию, адаптированную под ваш бизнес.







