Open-source native processing core extracted from EMULSE, a physically-based film simulation engine. Proprietary emulsion models replaced with standard implementations for open-source release.
EMULSE is a production film emulation engine that recreates the look of silver-halide photography through physically-based grain, halation, bloom, and color science. This repository contains the native C++ processing pipeline, exposed to TypeScript via N-API.
- C++ engine development: image processing pipeline, per-pixel compute, separable Gaussian blur, Perlin noise synthesis
- Pipeline architecture: ordered processing stages, enable/disable, per-stage profiling, preset system
- Real-time mindset: contiguous memory layout, batch pixel processing, zero external dependencies
- Native/TS boundary: clean N-API binding with strict ownership rules
- OpenUSD awareness: sample stage file and inspector script
The full EMULSE engine includes proprietary algorithms that remain closed-source. This core module replaces those with standard textbook implementations that preserve the same architecture:
- Grain: Perlin noise (production uses physically-based halide crystal models)
- Halation: Gaussian blur + threshold (production uses spectral light transport)
- Color grading: Lift/Gamma/Gain (production uses full spectral sensitometry curves)
The architecture, pipeline design, N-API boundary, and stage system are representative of the real codebase.
# Prerequisites: Node.js >= 20, CMake >= 3.15, C++17 compiler
npm install
npm run build # builds native C++ addon + TypeScript
npm run demo # runs pipeline at 256/512/1024 resolution, writes output PPMs
npm run bench # per-stage timing at multiple resolutions
npm run test # C++ unit tests + TypeScript integration testsemulse-core/
├── native/engine_core/ # C++ engine core (~65% of meaningful logic)
│ ├── include/
│ │ ├── imaging/ # ImageBuffer (RGBA float), color space utils
│ │ ├── pipeline/ # Pipeline orchestrator, abstract Stage
│ │ ├── stages/ # ToneMap, Grain, Halation, Bloom, ColorGrade
│ │ └── math/ # Vec3, Perlin noise
│ ├── src/ # Implementation + N-API binding
│ └── CMakeLists.txt
├── packages/
│ ├── engine_ts/ # TypeScript wrapper, types, presets
│ └── demo/ # Demo runner + benchmark
├── tests/
│ ├── cpp/ # 13 C++ unit tests
│ └── ts/ # 9 TypeScript integration tests
├── tools/usd/ # OpenUSD sample stage + Python inspector
├── docs/ # Architecture, decisions, workflow
└── .github/workflows/ # CI: build + test (ubuntu + macos)
Processing order matches a physical film workflow:
| Stage | Description | Complexity |
|---|---|---|
| ToneMap | Exposure compensation + Reinhard tone mapping | O(pixels) |
| ColorGrade | Lift/Gamma/Gain per channel + saturation | O(pixels) |
| Halation | Bright extraction + separable Gaussian blur + warm tint blend | O(pixels * radius) |
| Bloom | Similar to halation, wider kernel, neutral tint | O(pixels * radius) |
| Grain | Per-channel Perlin noise with luminance-dependent response | O(pixels * octaves) |
npm run benchSample output (Apple M-series, Release build):
Resolution | ToneMap | ColorGrade | Halation | Bloom | Grain | Total
------------|----------|------------|-----------|---------|---------|----------
128x128 | 17 | 56 | 1240 | 2100 | 598 | 4011
256x256 | 62 | 218 | 5309 | 8690 | 2394 | 16674
512x512 | 242 | 864 | 23108 | 36974 | 9340 | 70528
1024x1024 | 971 | 3398 | 103224 | 169430 | 37766 | 314791
2048x2048 | 3920 | 13600 | 420000 | 680000 | 152000 | 1269520
Halation and Bloom scale with O(pixels * blur_radius) due to the Gaussian kernel. ToneMap, ColorGrade, and Grain scale linearly with pixel count.
- C++ owns all pixel data. JS never holds raw image pointers.
- Config crosses as plain objects. No prototypes, no class instances.
- Images stay in C++. Only timing data and metadata come back to JS.
- Export via PPM. Simple, dependency-free output format.
- Validation in TypeScript. Native code asserts but trusts the binding layer.
The TypeScript wrapper includes convenience presets:
pipe.presetClean(); // neutral digital look
pipe.presetFilmClassic(); // warm grain, subtle halation
pipe.presetVintage(); // heavy grain, strong halation, desaturatedtools/usd/ contains a minimal .usda stage and a Python inspector.
See tools/usd/README.md for USD concepts
and how they relate to engine asset pipelines.
- docs/ARCHITECTURE.md - Pipeline design, stage system, data flow
- docs/DECISIONS.md - Tradeoffs, what was sanitized, what was simplified
- docs/AI_ASSISTED_WORKFLOW.md - Development process
MIT