Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jtapieromalambo-ctrl/Signia/llms.txt

Use this file to discover all available pages before exploring further.

Static file handling in Signia is deliberately conservative. Django’s collectstatic command gathers every CSS file, JavaScript module, image, and MediaPipe WebAssembly binary into a single /staticfiles/ directory, and WhiteNoise serves that directory directly from the Gunicorn process — no separate CDN or Nginx reverse proxy is required. The storage backend is carefully chosen to avoid a hard-to-debug breakage in the browser-side hand-landmark detector: the wrong backend silently renames files in a way that makes MediaPipe’s WASM loader fail at runtime.

Development vs. Production

In development (DEBUG=True), Django’s built-in static file server handles all requests to /static/. No extra configuration is needed: files are read directly from the static/ directory at the project root and from each installed app’s static/ subdirectory.
python manage.py runserver
# Static files served automatically from /static/
STATICFILES_DIRS in settings.py points Django at the project-level static directory:
STATICFILES_DIRS = [BASE_DIR / 'static']

Why CompressedStaticFilesStorage — Not ManifestStaticFilesStorage

This is the most critical static-file decision in the project. Django ships with ManifestStaticFilesStorage as its production-grade backend. WhiteNoise provides ManifestStaticFilesStorage as well. Neither is used here, and changing this setting will break the sign-language recognition module in the browser. Here is what happens with each backend:
Manifest mode post-processes every collected file and appends a content hash to its filename:
vision_wasm_internal.wasm
→ vision_wasm_internal.abc123de.wasm   ← renamed
A staticfiles.json manifest maps original names to hashed names, and Django’s {% static %} template tag reads this manifest to rewrite URLs in templates. The problem is that the WASM loader inside MediaPipe is not a Django template. It is a JavaScript module that constructs the WASM filename itself at runtime using hard-coded string literals:
// Inside vision_bundle.mjs — beyond your control
wasmPath = baseUrl + 'wasm/vision_wasm_internal.wasm';
After collectstatic, that file has been renamed to vision_wasm_internal.<hash>.wasm. The browser requests vision_wasm_internal.wasm, receives a 404, and the HandLandmarker never initialises — silently breaking all camera recognition.
Do not change the "staticfiles" entry in STORAGES. Switching to ManifestStaticFilesStorage or WhiteNoiseStorage (which also enables manifest mode) will rename the WASM files and break camera-based hand recognition for every user. The symptom is silent — the page loads, the camera starts, but no landmarks are ever detected.

Skipping Compression for WASM and Task Files

Even though CompressedStaticFilesStorage preserves filenames, WhiteNoise would still try to gzip .wasm files by default — binary formats that are already compact. More critically, Chromium and other browsers issue HTTP range requests for large binary resources. A gzip-compressed file cannot satisfy a range request, which triggers a 416 Requested Range Not Satisfiable error and prevents the WASM module from loading. WHITENOISE_SKIP_COMPRESS_EXTENSIONS tells WhiteNoise never to create compressed copies of these extensions:
WHITENOISE_SKIP_COMPRESS_EXTENSIONS = (
    'jpg', 'jpeg', 'png', 'gif', 'webp', 'zip', 'gz', 'tgz', 'bz2', 'tbz', 'xz',
    'br', 'swf', 'flv', 'woff', 'woff2', '3gp', '3gpp', 'asf', 'avi', 'm4v',
    'mov', 'mp4', 'mpeg', 'mpg', 'webm', 'wmv', 'wasm', 'task'
)
The wasm and task entries at the end are the ones added specifically for MediaPipe. The .task extension is used by MediaPipe’s model asset files. Additionally, settings.py registers the correct MIME types so browsers and WhiteNoise agree on the content type:
import mimetypes
mimetypes.add_type("application/wasm", ".wasm")
mimetypes.add_type("application/octet-stream", ".task")

MediaPipe Static File Structure

The bundled MediaPipe Vision library lives under static/js/vendor/mediapipe/ and is checked into the repository so that collectstatic can find it without a separate download step at build time.
static/
└── js/
    └── vendor/
        └── mediapipe/
            ├── vision_bundle.cjs                        ← CommonJS bundle
            ├── vision_bundle.mjs                        ← ES module bundle
            └── wasm/
                ├── vision_wasm_internal.js              ← WASM glue (SIMD)
                ├── vision_wasm_internal.wasm            ← WASM binary (SIMD)
                ├── vision_wasm_nosimd_internal.js       ← WASM glue (no SIMD)
                └── vision_wasm_nosimd_internal.wasm     ← WASM binary (no SIMD)
MediaPipe automatically selects between the SIMD and non-SIMD variants based on browser capability. Both variants must be present and must retain their exact filenames — which is precisely why CompressedStaticFilesStorage is required.

Media Files (/media/)

The /media/ directory stores sign videos uploaded by administrators through the /admin-videos/ panel. It is separate from static files and is never processed by collectstatic. In development, Django serves media files automatically when DEBUG=True:
# urls.py (DEBUG=True branch)
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
In production, a re_path fallback in urls.py routes media requests through Django’s serve view:
# urls.py (production branch)
else:
    urlpatterns += [
        re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    ]
Serving media through Django’s serve view is acceptable for Signia because the /media/ folder holds reference sign videos that are read once during admin training and then deleted from disk. Long-term video storage should be moved to S3 or Cloudinary if the media folder needs to persist across deployments. See the Railway deployment guide for details on ephemeral storage limitations.

Build docs developers (and LLMs) love