Modern device interfaces increasingly demand sophisticated visual displays that go beyond static content. The KOS Canvas Renderer System addresses this need by providing a comprehensive framework for streaming dynamic visual content to pixel-based displays. From simple LED matrices to complex digital signage, this system handles everything from basic color displays to complex video sequences with real-time control and event coordination.
Architecture of Visual Streaming
The Canvas Renderer System operates through a two-tier architecture that separates pattern management from actual rendering operations. The CanvasDispatcher serves as the orchestration layer, managing rendering patterns and coordinating multiple display endpoints, while individual CanvasRenderer instances handle the streaming and lifecycle of visual content to specific displays.
This separation enables sophisticated scenarios where multiple displays can be coordinated, patterns can be reused across different endpoints, and complex sequences can be managed through centralized control:
@kosModel({ modelTypeId: "digital-signage-controller", singleton: false })
export class DigitalSignageController implements IKosDataModel {
@kosDependency({ modelType: CanvasDispatcher.type })
private dispatcher!: CanvasDispatcherModel;
async load() {
// Configure multiple display endpoints
this.dispatcher.addNamedEndpoint("main-screen", {
id: "main-screen",
apiPath: "/api/system/canvas/main",
width: 128,
height: 64,
fps: 30
});
this.dispatcher.addNamedEndpoint("status-panel", {
id: "status-panel",
apiPath: "/api/system/canvas/status",
width: 32,
height: 16,
fps: 15
});
}
}The dispatcher creates and manages renderer instances automatically when endpoints are added, handling the complexity of coordinating multiple displays while providing a unified control interface.
Pattern-Based Content Management
Rather than requiring custom rendering code for every visual effect, the Canvas Renderer System uses a pattern-based approach that defines reusable rendering configurations. These patterns encapsulate both the rendering type and its associated data, enabling content to be applied consistently across different displays:
// Video patterns with flexible looping
this.dispatcher.addPattern("product-demo", {
id: "product-demo",
type: "video",
data: {
src: "/assets/product-demo.mp4",
loop: 3, // Play three times
startTime: 2.5, // Start at 2.5 seconds
endTime: 30.0 // End at 30 seconds
}
});
// Matrix effect with customizable parameters
this.dispatcher.addPattern("system-status", {
id: "system-status",
type: "matrix",
data: {
speed: 2,
fallChance: 0.8,
baseColor: { r: 0, g: 255, b: 0 } // Green Matrix rain
}
});
// Scrolling text optimized for LED displays
this.dispatcher.addPattern("status-message", {
id: "status-message",
type: "text",
data: {
text: "System Online - All Services Running",
fontSize: 8, // Optimized for LED panels
scroll: {
enabled: true,
direction: "left",
speed: 50
},
color: "#00ff00",
loop: true
}
});This pattern system enables rapid content creation and modification without requiring code changes. Patterns can be combined, sequenced, and modified at runtime through HTTP API calls, providing the flexibility needed for dynamic display management.
Complex Visual Compositions
The system supports sophisticated visual compositions through sequence patterns and overlay renderers. Sequence patterns enable multiple visual elements to play in order with configurable transitions, while overlay renderers provide persistent status indicators that remain visible during dynamic content changes:
// Sequence combining multiple visual elements
this.dispatcher.addPattern("welcome-sequence", {
id: "welcome-sequence",
type: "sequence",
data: {
patterns: ["company-logo", "product-demo", "call-to-action"],
loop: false,
transitionDuration: 1000,
patternOverrides: {
"product-demo": { loop: 1 } // Override to play once in sequence
}
}
});
// Overlay renderer for persistent status indicators
this.dispatcher.addPattern("status-with-overlay", {
id: "status-with-overlay",
type: "overlay",
data: {
baseRenderer: {
type: "text",
data: {
text: "Processing transaction...",
scroll: { enabled: true, direction: "left", speed: 40 }
}
},
overlays: [
{
src: "/assets/icons/wifi-connected.svg",
size: 8,
position: "top-left"
},
{
src: "/assets/icons/power-status.svg",
size: 8,
position: "top-right"
}
],
persistOverlaysOnComplete: true
}
});These composition capabilities enable sophisticated information displays where status indicators remain consistent while dynamic content changes, or where complex narrative sequences guide users through different interface states.
Event-Driven Content Control
The Canvas Renderer System integrates deeply with KOS’s event architecture, enabling reactive content management that responds to system state changes and user interactions. Completion events from finite renderers enable sophisticated content sequencing:
@kosModel({ modelTypeId: "interactive-display-controller", singleton: false })
export class InteractiveDisplayController implements IKosDataModel {
@kosDependency({ modelType: CanvasDispatcher.type })
private dispatcher!: CanvasDispatcherModel;
private currentPhase: "attract" | "interaction" | "completion" = "attract";
@kosTopicHandler("/canvas/renderer/completed")
onRendererComplete(event: any) {
if (event.endpoint === "main-display") {
this.handleSequenceCompletion(event);
}
}
private async handleSequenceCompletion(event: any) {
switch (this.currentPhase) {
case "attract":
if (event.rendererId === "attract-sequence") {
await this.startInteractionMode();
}
break;
case "interaction":
if (event.rendererId === "selection-timeout") {
await this.returnToAttract();
}
break;
case "completion":
if (event.rendererId === "thank-you") {
await this.resetToAttract();
}
break;
}
}
private async startInteractionMode() {
this.currentPhase = "interaction";
await this.applyPattern("main-display", "interactive-menu");
}
}This event-driven approach enables complex state machines where visual content responds automatically to interaction patterns, system events, and operational requirements.
Runtime Content Modification
One of the system’s most powerful features is its ability to modify content parameters at runtime without requiring pattern redefinition. This enables dynamic personalization, real-time status updates, and context-sensitive content adaptation:
// Dynamically change matrix effect color based on system status
async updateSystemStatus(status: "normal" | "warning" | "error") {
const colorMap = {
normal: { r: 0, g: 255, b: 0 }, // Green
warning: { r: 255, g: 165, b: 0 }, // Orange
error: { r: 255, g: 0, b: 0 } // Red
};
await kosFetch({
method: "POST",
url: "/api/ui/canvas/pattern/status-display/system-matrix",
body: {
baseColor: colorMap[status],
speed: status === "error" ? 4 : 2, // Faster animation for errors
fallChance: status === "error" ? 0.95 : 0.7
}
});
}
// Update scrolling text content dynamically
async updateStatusMessage(message: string, urgent: boolean = false) {
await kosFetch({
method: "POST",
url: "/api/ui/canvas/pattern/info-panel/status-text",
body: {
text: message,
color: urgent ? "#ff0000" : "#00ff00",
scroll: {
speed: urgent ? 100 : 50 // Faster scroll for urgent messages
}
}
});
}This runtime modification capability enables responsive interfaces that adapt to changing conditions without requiring application restarts or pattern redeployment.
Hardware Integration Patterns
The Canvas Renderer System is designed specifically for integration with physical display hardware, providing optimized rendering for LED matrices, digital signage displays, and other pixel-based output devices. The system handles pixel data streaming, frame rate management, and hardware-specific optimizations:
// LED matrix optimization with appropriate frame rates
this.dispatcher.addNamedEndpoint("led-matrix", {
id: "led-matrix",
apiPath: "/api/hardware/matrix/stream",
width: 32, // Match physical LED matrix dimensions
height: 16,
fps: 10, // Appropriate for LED refresh rates
durationMs: null // Infinite streaming
});
// High-resolution signage display
this.dispatcher.addNamedEndpoint("4k-display", {
id: "4k-display",
apiPath: "/api/hardware/signage/stream",
width: 3840,
height: 2160,
fps: 30, // Smooth video playback
durationMs: null
});
// Text rendering optimized for small LED panels
this.dispatcher.addPattern("led-text", {
id: "led-text",
type: "text",
data: {
text: "WELCOME",
fontSize: 8, // Sweet spot for 32x16 panels
fontFamily: "monospace", // Pixel-perfect rendering
color: "#ffffff" // High contrast
}
});The system automatically handles pixel data extraction and streaming to configured API endpoints, enabling seamless integration with hardware drivers and display controllers.
Custom Renderer Development
For specialized visual effects beyond the built-in renderer types, the system supports custom renderer development through well-defined interfaces. Custom renderers can implement complex animations, data visualizations, or hardware-specific effects:
import type { Renderer, DoneAware } from "@kosdev-code/kos-ui-sdk";
export class DataVisualizationRenderer implements Renderer, DoneAware {
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
private animationFrame = 0;
private completionCallback: (() => void) | null = null;
constructor(private data: { dataSource: string; duration: number }) {
this.canvas = document.createElement("canvas");
const ctx = this.canvas.getContext("2d");
if (!ctx) throw new Error("2D canvas not supported");
this.ctx = ctx;
}
reset(): void {
this.animationFrame = 0;
}
renderFrame(width: number, height: number): void {
this.canvas.width = width;
this.canvas.height = height;
// Clear canvas
this.ctx.fillStyle = "black";
this.ctx.fillRect(0, 0, width, height);
// Render data visualization based on current frame
this.renderDataVisualization(width, height);
this.animationFrame++;
// Check completion
if (this.animationFrame * (1000/30) >= this.data.duration) {
if (this.completionCallback) {
this.completionCallback();
}
}
}
onDone(callback: () => void): void {
this.completionCallback = callback;
}
isOneShot(): boolean {
return true;
}
readPixels(width: number, height: number): Uint8ClampedArray {
return this.ctx.getImageData(0, 0, width, height).data;
}
dispose(): void {
this.completionCallback = null;
this.canvas.remove();
}
private renderDataVisualization(width: number, height: number): void {
// Custom visualization logic here
const progress = this.animationFrame / (this.data.duration / (1000/30));
// Example: animated bar chart
this.ctx.fillStyle = "#00ff00";
const barHeight = (Math.sin(progress * Math.PI) * height * 0.8);
this.ctx.fillRect(width * 0.4, height - barHeight, width * 0.2, barHeight);
}
}Custom renderers integrate seamlessly with the pattern system and can be registered for use across all display endpoints, enabling specialized visual effects while maintaining the system’s unified control interface.
Performance and Resource Management
The Canvas Renderer System incorporates sophisticated performance optimization strategies essential for real-time visual streaming. Frame rate adaptation, resource pooling, and automatic cleanup ensure smooth operation across varying hardware capabilities:
The system automatically adjusts rendering complexity based on configured frame rates and display dimensions, while completion events enable efficient resource cleanup when finite content sequences end. This approach ensures that visual quality scales appropriately with available processing power while maintaining responsive user interfaces.
The Canvas Renderer System represents a comprehensive solution for dynamic visual content management in KOS applications. By combining pattern-based content management with event-driven control and hardware integration capabilities, it enables sophisticated visual interfaces that respond dynamically to system state and user interaction while maintaining the performance and reliability required for production device deployments.
