Repository navigation
Accessibility
The Accessibility System provides engine-wide runtime accessibility features including colorblind simulation and correction (daltonization matrices for 5 modes), subtitle rendering with speaker tags, high-contrast UI, large text scaling, screen reader hooks, reduced motion, and one-handed input presets. It lives in the Spark::Accessibility namespace.
Source: SparkEngine/Source/Engine/Accessibility/AccessibilitySystem.h
| Class / Struct | Responsibility |
|---|---|
AccessibilitySystem |
Singleton managing all accessibility features: settings, colorblind filters, subtitles, and feature flags |
AccessibilitySettings |
Struct holding all user-facing accessibility preferences |
SubtitleEntry |
A single subtitle to display on screen with speaker tag, duration, and styling |
ColorblindMode |
Enum selecting the active colorblind simulation/correction mode |
OneHandedMode |
Enum selecting one-handed input layout (none, left, right) |
enum class ColorblindMode : uint8_t
{
None = 0, // No correction applied
Protanopia, // Red-blind (L-cone deficiency)
Deuteranopia, // Green-blind (M-cone deficiency)
Tritanopia, // Blue-blind (S-cone deficiency)
Achromatopsia // Total color blindness (monochromacy)
};enum class OneHandedMode : uint8_t
{
None = 0,
LeftHanded,
RightHanded
};struct AccessibilitySettings
{
ColorblindMode colorblindMode = ColorblindMode::None;
float colorblindStrength = 1.0f; // Correction intensity (0 = off, 1 = full)
bool highContrastUI = false; // High-contrast editor/game UI
bool largeText = false; // Force larger default font size
float textScaleFactor = 1.0f; // Global text scale multiplier
bool subtitlesEnabled = false; // Show subtitles for dialogue/audio
float subtitleBackgroundOpacity = 0.7f; // Subtitle background alpha (0-1)
bool screenReaderEnabled = false; // Emit text descriptions for UI elements
bool reducedMotion = false; // Disable screen shake, reduce particles
bool inputRemappingEnabled = false; // Allow runtime key remapping
OneHandedMode oneHandedMode = OneHandedMode::None;
};struct SubtitleEntry
{
std::string text; // The subtitle text content
std::string speaker; // Speaker name/tag (empty if narration)
float duration = 3.0f; // Display duration in seconds
float fontSize = 18.0f; // Font size in points
uint32_t color = 0xFFFFFFFF; // RGBA packed color
float remainingTime = 0.0f; // Internal: time left before removal
};#include "Engine/Accessibility/AccessibilitySystem.h"
auto& access = Spark::Accessibility::AccessibilitySystem::GetInstance();
access.Initialize();using namespace Spark::Accessibility;
auto& access = AccessibilitySystem::GetInstance();
AccessibilitySettings s = access.GetSettings();
s.colorblindMode = ColorblindMode::Deuteranopia;
s.colorblindStrength = 0.8f; // 80% correction intensity
access.SetSettings(s);float rgbIn[3] = {1.0f, 0.0f, 0.0f}; // Pure red
float rgbOut[3];
access.ApplyColorblindFilter(rgbOut, rgbIn);
// rgbOut now contains the daltonized colorauto settings = access.GetSettings();
settings.subtitlesEnabled = true;
settings.textScaleFactor = 1.5f; // 150% text scale
access.SetSettings(settings);
Spark::Accessibility::SubtitleEntry sub;
sub.text = "Watch out for the enemy ahead!";
sub.speaker = "Squad Leader";
sub.duration = 4.0f;
sub.fontSize = 20.0f;
sub.color = 0xFFFF00FF; // Yellow, full alpha
access.PushSubtitle(sub);// In your main loop -- expires subtitles whose duration has elapsed
access.Update(dt);const auto& subtitles = access.GetActiveSubtitles();
for (const auto& sub : subtitles)
{
RenderSubtitle(sub.text, sub.speaker, sub.fontSize,
sub.color, sub.remainingTime / sub.duration);
}auto settings = access.GetSettings();
settings.highContrastUI = true;
settings.reducedMotion = true;
access.SetSettings(settings);
// In your VFX/camera system:
if (access.GetSettings().reducedMotion)
{
// Skip screen shake, reduce particle counts, disable camera bob
cameraShakeIntensity = 0.0f;
particleSpawnRate *= 0.25f;
}auto settings = access.GetSettings();
settings.oneHandedMode = Spark::Accessibility::OneHandedMode::LeftHanded;
settings.inputRemappingEnabled = true;
access.SetSettings(settings);
// The input system should query this setting to apply
// one-handed presets (e.g., remap right-hand keys to left side)The system pre-builds 3x3 correction matrices for each ColorblindMode during Initialize(). These are row-major float arrays.
auto matrix = Spark::Accessibility::AccessibilitySystem::GetColorblindCorrectionMatrix(
Spark::Accessibility::ColorblindMode::Protanopia
);
// matrix is std::array<float, 9> in row-major order
// matrix[row * 3 + col]| Mode | Description | Matrix (row-major) |
|---|---|---|
None |
Identity | 1,0,0, 0,1,0, 0,0,1 |
Protanopia |
Red-blind | 0.567,0.433,0.000, 0.558,0.442,0.000, 0.000,0.242,0.758 |
Deuteranopia |
Green-blind | 0.625,0.375,0.000, 0.700,0.300,0.000, 0.000,0.300,0.700 |
Tritanopia |
Blue-blind | 0.950,0.050,0.000, 0.000,0.433,0.567, 0.000,0.475,0.525 |
Achromatopsia |
Monochromacy | 0.2126,0.7152,0.0722, 0.2126,0.7152,0.0722, 0.2126,0.7152,0.0722 |
The Achromatopsia matrix uses Rec. 709 luminance coefficients for perceptually accurate grayscale conversion.
Pass the correction matrix as a uniform to your post-processing shader:
auto mat = access.GetColorblindCorrectionMatrix(access.GetSettings().colorblindMode);
shader->SetUniformMatrix3f("u_colorblindMatrix", mat.data());
shader->SetUniform1f("u_colorblindStrength", access.GetSettings().colorblindStrength);In the shader:
float3 corrected = mul(float3x3(u_colorblindMatrix), originalColor.rgb);
finalColor.rgb = lerp(originalColor.rgb, corrected, u_colorblindStrength);The system includes a generic feature flag mechanism for extensibility:
// Enable a custom accessibility feature
access.SetFeatureEnabled("screen_magnifier", true);
access.SetFeatureEnabled("dyslexia_font", true);
// Check if a feature is enabled
if (access.IsFeatureEnabled("screen_magnifier"))
{
// Apply magnification to the UI
}
// Disable a feature
access.SetFeatureEnabled("screen_magnifier", false);Feature flags are stored in an std::unordered_map<std::string, bool> and are cleared on Shutdown().
| Field | Type | Default | Description |
|---|---|---|---|
colorblindMode |
ColorblindMode |
None |
Active colorblind correction mode |
colorblindStrength |
float |
1.0 |
Correction intensity (0 = off, 1 = full) |
highContrastUI |
bool |
false |
Force high-contrast colors in UI |
largeText |
bool |
false |
Force larger default font size |
textScaleFactor |
float |
1.0 |
Global text scale multiplier for all UI text |
subtitlesEnabled |
bool |
false |
Display subtitles for dialogue and audio |
subtitleBackgroundOpacity |
float |
0.7 |
Background alpha for subtitle boxes (0-1) |
screenReaderEnabled |
bool |
false |
Emit text descriptions for UI elements |
reducedMotion |
bool |
false |
Disable screen shake, camera bob, reduce particles |
inputRemappingEnabled |
bool |
false |
Allow runtime key remapping |
oneHandedMode |
OneHandedMode |
None |
One-handed input preset selection |
-
PushSubtitle()only adds a subtitle ifsubtitlesEnabledis true in current settings - The subtitle's
fontSizeis automatically multiplied bytextScaleFactorwhen pushed -
Update(dt)decrementsremainingTimeeach frame and removes expired entries - Active subtitles are accessible via
GetActiveSubtitles()for rendering
| Method | Description |
|---|---|
Console_GetStatus() |
Returns initialization state, colorblind mode, subtitle status (with active count), high contrast, reduced motion, and text scale |
Example output:
AccessibilitySystem: initialized | Colorblind: Deuteranopia | Subtitles: on (active: 2) | HighContrast: off | ReducedMotion: on | TextScale: 1.500000
The accessibility feature set is designed to support compliance with major platform and regulatory requirements:
| Standard | Relevant Features |
|---|---|
| EU European Accessibility Act (EAA) | Colorblind correction, subtitles, screen reader hooks, text scaling, high contrast |
| Xbox Accessibility Guidelines (XAG) | Colorblind modes, subtitle customization, reduced motion, input remapping |
| PlayStation Certification | Subtitle support, text scaling, colorblind options |
| WCAG 2.1 (web-adjacent) | High contrast, text scaling, reduced motion, keyboard navigation |
Use the settings struct to implement per-platform compliance checklists. The screenReaderEnabled flag is a hook point for platform-specific screen reader integration (e.g., Windows Narrator, VoiceOver on macOS).
Apply colorblind correction as a post-processing pass:
void PostProcessPass::ApplyAccessibility()
{
const auto& settings = AccessibilitySystem::GetInstance().GetSettings();
if (settings.colorblindMode != ColorblindMode::None)
{
auto mat = AccessibilitySystem::GetColorblindCorrectionMatrix(settings.colorblindMode);
m_colorblindShader->SetUniformMatrix3f("u_matrix", mat.data());
m_colorblindShader->SetUniform1f("u_strength", settings.colorblindStrength);
m_colorblindShader->Apply();
}
}Query settings for text rendering:
float GetEffectiveFontSize(float baseFontSize)
{
const auto& settings = AccessibilitySystem::GetInstance().GetSettings();
float size = baseFontSize * settings.textScaleFactor;
if (settings.largeText)
size = std::max(size, 24.0f);
return size;
}
bool ShouldUseHighContrast()
{
return AccessibilitySystem::GetInstance().GetSettings().highContrastUI;
}Push subtitles from dialogue events:
void OnDialogueLine(const std::string& speaker, const std::string& text, float duration)
{
Spark::Accessibility::SubtitleEntry sub;
sub.speaker = speaker;
sub.text = text;
sub.duration = duration;
sub.fontSize = 18.0f;
sub.color = GetSpeakerColor(speaker);
AccessibilitySystem::GetInstance().PushSubtitle(sub);
}Apply one-handed mode presets:
if (access.GetSettings().oneHandedMode == OneHandedMode::LeftHanded)
{
// Remap WASD to arrow keys, right-hand actions to left-hand keys
actionMap.RegisterAction("MoveForward", PlatformKeyCode::Up);
actionMap.RegisterAction("Fire", PlatformKeyCode::Q);
}Respect reduced motion:
if (access.GetSettings().reducedMotion)
{
screenShakeAmount = 0.0f;
particleSpawnMultiplier = 0.25f;
disableCameraBob = true;
skipFlashEffects = true;
}| Method | Signature | Description |
|---|---|---|
GetInstance |
static AccessibilitySystem& GetInstance() |
Get the singleton instance |
Initialize |
void Initialize() |
Initialize system, build color matrices, reset settings |
Shutdown |
void Shutdown() |
Clear subtitles, feature flags, reset state |
Update |
void Update(float dt) |
Per-frame update; expires subtitles |
GetSettings |
const AccessibilitySettings& GetSettings() const |
Get current settings (read-only) |
SetSettings |
void SetSettings(const AccessibilitySettings& settings) |
Apply new settings |
ApplyColorblindFilter |
void ApplyColorblindFilter(float* rgbOut, const float* rgbIn) const |
Apply daltonization to an RGB triplet |
GetColorblindCorrectionMatrix |
static std::array<float, 9> GetColorblindCorrectionMatrix(ColorblindMode mode) |
Get the 3x3 correction matrix for a mode |
PushSubtitle |
void PushSubtitle(const SubtitleEntry& entry) |
Push a subtitle for display (respects subtitlesEnabled) |
GetActiveSubtitles |
const std::vector<SubtitleEntry>& GetActiveSubtitles() const |
Get all currently visible subtitles |
IsFeatureEnabled |
bool IsFeatureEnabled(std::string_view feature) const |
Check if a named feature flag is enabled |
SetFeatureEnabled |
void SetFeatureEnabled(std::string_view feature, bool enabled) |
Enable or disable a named feature flag |
Console_GetStatus |
std::string Console_GetStatus() const |
Human-readable status string |
AccessibilitySystem is not thread-safe. SetSettings(), PushSubtitle(), Update(), and SetFeatureEnabled() all modify internal state without synchronization. Call from the main thread only.
GetColorblindCorrectionMatrix() is a static method with no mutable state and is safe to call from any thread.
The cached color matrices (m_colorMatrices) are built once during Initialize() and only read thereafter, so they are effectively immutable during normal operation.
- Platform-Input -- Input remapping and one-handed mode integration
- Telemetry-System -- Track accessibility feature usage
- UI-System -- UI rendering with text scaling and high contrast
- Dialogue-System -- Subtitle integration with dialogue
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers