Skip to content

SimplyPrint Emulator Feasibility Study

Project: Forma3D.Connect
Version: 1.0
Date: January 28, 2026
Status: Research / Proposal


Executive Summary

This document evaluates the feasibility of building a 3D printer emulator to test Forma3D.Connect software integration with SimplyPrint without requiring physical printer hardware. Based on research conducted in January 2026, a fully functional virtual printer emulator is not only feasible but already exists as an official example in the SimplyPrint WebSocket client library.

Recommendation

Leverage the existing simplyprint-ws-client Python library to create a virtual printer emulator for testing. The library provides: - A complete VirtualClient example implementation - Full WebSocket communication with SimplyPrint servers - Realistic simulation of printer states, temperatures, and print progress - Automatic setup code generation for registering the virtual printer in SimplyPrint


Problem Statement

Testing Forma3D.Connect's integration with SimplyPrint currently requires: - Physical 3D printer hardware - Maintaining printer connectivity and operational status - Executing actual print jobs for end-to-end testing - Limited ability to test edge cases (errors, failures, specific states)

A virtual printer emulator would enable: - Development without physical hardware - Automated integration testing in CI/CD pipelines - Testing of edge cases and error scenarios - Parallel testing with multiple virtual printers - Consistent, reproducible test environments


SimplyPrint WebSocket Client Library

Overview

SimplyPrint provides an official Python library for connecting custom printer implementations to their platform.

Property Details
Package Name simplyprint-ws-client
Current Version 1.0.1rc42 (pre-release)
Python Requirement Python 3.9+
License AGPL-3.0-or-later
Repository github.com/SimplyPrint/simplyprint-ws-client
PyPI pypi.org/project/simplyprint-ws-client

Key Dependencies

psutil>=6.0.0
pydantic>=2.11.7
aiohttp>=3.12.15
click>=8.2.1
sentry-sdk>=2.35.1

Existing Virtual Printer Implementation

The library includes a complete VirtualClient example that already implements a functional virtual printer emulator.

Source Location

https://github.com/SimplyPrint/simplyprint-ws-client/blob/main/example/virtual_client.py

Features Implemented

Feature Implementation Status Details
Printer identification ✅ Complete Name, version, firmware info
Temperature simulation ✅ Complete Bed and tool temperatures with realistic smoothing
Print job progress ✅ Complete Exponential smoothing for realistic progress
File download simulation ✅ Complete Simulates download progress
G-code command handling ✅ Complete Temperature commands (M104, M140)
Print lifecycle ✅ Complete Start, cancel, complete states
Virtual camera ✅ Complete Static test image feed
Material/filament info ✅ Complete Multiple materials per tool
Mesh data generation ✅ Complete Fake bed level visualization data
Status transitions ✅ Complete OPERATIONAL → PRINTING → CANCELLING, etc.

Code Architecture

class VirtualClient(DefaultClient[VirtualConfig], ClientCameraMixin):
    """Virtual 3D printer that simulates all printer behaviors"""

    # Core lifecycle methods
    async def init(self): ...           # Initialize printer state
    async def tick(self, _): ...        # Update temperatures, progress
    async def halt(self): ...           # Stop operations
    async def teardown(self): ...       # Cleanup

    # Event handlers
    async def on_connected(self): ...        # WebSocket connected
    async def on_gcode(self, data): ...      # G-code commands received
    async def on_file(self, data): ...       # File download request
    async def on_start_print(self, _): ...   # Print job started
    async def on_cancel(self, _): ...        # Print cancelled

Temperature Simulation

The virtual client uses exponential smoothing for realistic temperature transitions:

def expt_smooth(target, actual, alpha, dt) -> float:
    return actual + (target - actual) * (1.0 - math.exp(-alpha * dt))

This creates natural heating/cooling curves that match real printer behavior.


Implementation Approach

Use the existing simplyprint-ws-client library directly:

# Install the library
pip install simplyprint-ws-client

# Or with uv (recommended)
uv pip install simplyprint-ws-client

Entry point example:

from simplyprint_ws_client import (
    ClientApp,
    ConfigManagerType,
    ClientSettings,
    ConnectionMode,
)
from simplyprint_ws_client.shared.logging import setup_logging

# Import VirtualClient from example or custom implementation
from virtual_client import VirtualClient, VirtualConfig

if __name__ == "__main__":
    settings = ClientSettings(
        name="Forma3D-VirtualPrinter",
        mode=ConnectionMode.MULTI,  # Support multiple virtual printers
        client_factory=VirtualClient,
        config_factory=VirtualConfig,
        allow_setup=True,
        config_manager_t=ConfigManagerType.JSON,
        development=True,  # Enable development mode
    )

    setup_logging(settings)
    app = ClientApp(settings)

    # Add a new virtual printer or use existing config
    if len(app.config_manager.get_all()) > 0:
        config = app.config_manager.get_all()[0]
    else:
        config = VirtualConfig.get_new()

    client = app.add(config)
    print(f"Setup code: {client.config.short_id}")
    app.run_blocking()

Option 2: Fork and Customize

Fork the simplyprint-ws-client repository and customize VirtualClient for specific testing needs:

  1. Add configurable failure scenarios
  2. Implement specific printer model behaviors
  3. Add telemetry/logging for test analysis
  4. Integrate with our test framework

Option 3: TypeScript/Node.js Implementation

If Python is not preferred, implement a Node.js client based on the WebSocket protocol:

// Conceptual - would require reverse-engineering the protocol
interface PrinterState {
    status: 'operational' | 'printing' | 'paused' | 'error';
    bedTemp: { actual: number; target: number };
    toolTemp: { actual: number; target: number };
    progress: number;
}

class VirtualPrinter {
    private ws: WebSocket;
    private state: PrinterState;

    async connect(): Promise<void> {
        // Connect to SimplyPrint WebSocket server
    }

    async tick(): Promise<void> {
        // Update state and send to server
    }
}

Note: This would require significant protocol reverse-engineering and is not recommended given the official Python library exists.


Setup and Registration Process

How Virtual Printer Registration Works

  1. First Run: Library generates a unique unique_id (UUID)
  2. Server Connection: Connects to SimplyPrint WebSocket servers
  3. Setup Code: Server assigns a short_id (e.g., "9ZZM")
  4. Account Linking: Use setup code at simplyprint.io/setup-guide
  5. Configuration Saved: Token and settings persist to JSON file

First Run Output Example

$ python -m example
PrinterConfig(
    id=0, 
    token='0', 
    name=None, 
    in_setup=None, 
    short_id=None, 
    unique_id='08086eeb-812b-4bda-9159-99852aff509b'
)

After Registration

$ python -m example
PrinterConfig(
    id=123456, 
    token='595a482a-745f-41a6-88c7-1402cffa1e9c', 
    name='Virtual Printer 1', 
    in_setup=False, 
    short_id='9ZZM',
    unique_id='08086eeb-812b-4bda-9159-99852aff509b'
)

Testing Scenarios Enabled

Basic Functionality

Scenario Virtual Printer Action
Printer online detection Connect and report OPERATIONAL status
Temperature monitoring Report simulated bed/tool temperatures
Print job assignment Accept file, simulate download, start print
Progress tracking Report incremental progress with realistic timing
Job completion Transition to completed state, cool down temps
Job cancellation Handle cancel command, transition to OPERATIONAL

Edge Cases and Error Scenarios

Scenario Implementation Approach
Connection loss Terminate WebSocket mid-operation
Temperature runaway Report abnormally high temperatures
Print failure Transition to ERROR state during print
File download failure Report failed download state
Firmware crash Disconnect unexpectedly
Multiple printers Run multiple VirtualClient instances

Automated Testing Integration

import pytest
import asyncio
from virtual_printer_emulator import VirtualPrinter

@pytest.fixture
async def virtual_printer():
    printer = VirtualPrinter()
    await printer.connect()
    yield printer
    await printer.disconnect()

@pytest.mark.asyncio
async def test_print_job_lifecycle(virtual_printer):
    # Start print
    await virtual_printer.start_print("test_file.gcode")
    assert virtual_printer.status == "printing"

    # Simulate progress
    await virtual_printer.simulate_progress(100)
    assert virtual_printer.status == "operational"
    assert virtual_printer.last_job_completed == True

Multi-Printer Testing

The library supports ConnectionMode.MULTI for simulating print farms:

settings = ClientSettings(
    name="Forma3D-PrintFarm",
    mode=ConnectionMode.MULTI,
    # ... other settings
)

app = ClientApp(settings)

# Add multiple virtual printers
for i in range(5):
    config = VirtualConfig.get_new()
    config.name = f"Virtual Printer {i+1}"
    app.add(config)

app.run_blocking()

This enables testing: - Print queue distribution - Load balancing across printers - Farm-wide status monitoring - Concurrent job handling


Integration with Forma3D.Connect

Architecture Diagram

┌─────────────────────────────────────────────────────────────────┐
│                     Development/Testing Environment             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────────────┐         ┌─────────────────────────────┐  │
│  │  Virtual Printer │ ◄─────► │    SimplyPrint Cloud       │  │
│  │  Emulator        │   WS    │    (Production Servers)     │  │
│  │  (Python)        │         │                             │  │
│  └──────────────────┘         └─────────────┬───────────────┘  │
│                                             │                   │
│                                             │ Webhooks/API      │
│                                             │                   │
│                               ┌─────────────▼───────────────┐  │
│                               │    Forma3D.Connect API      │  │
│                               │    (NestJS)                 │  │
│                               └─────────────┬───────────────┘  │
│                                             │                   │
│                               ┌─────────────▼───────────────┐  │
│                               │    Forma3D.Connect Web      │  │
│                               │    (React PWA)              │  │
│                               └─────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Proposed Directory Structure

forma-3d-connect/
├── tools/
│   └── virtual-printer/
│       ├── pyproject.toml
│       ├── README.md
│       ├── src/
│       │   └── virtual_printer/
│       │       ├── __init__.py
│       │       ├── __main__.py
│       │       ├── client.py          # Custom VirtualClient
│       │       ├── config.py          # Custom config with scenarios
│       │       └── scenarios/
│       │           ├── happy_path.py
│       │           ├── error_states.py
│       │           └── print_farm.py
│       └── tests/
│           └── test_scenarios.py

Feasibility Assessment

Technical Feasibility: ✅ HIGH

Factor Assessment
Library maturity Pre-release but functional, actively developed
Documentation Tutorial available, example code provided
Protocol stability WebSocket protocol is stable
Python compatibility Python 3.9+ widely available
Async support Full asyncio support

Implementation Effort: LOW

Task Estimated Effort
Install and configure library 1-2 hours
Register virtual printer 30 minutes
Basic testing setup 2-4 hours
Custom scenarios 4-8 hours
CI/CD integration 4-8 hours
Total 12-24 hours

Risks and Mitigations

Risk Likelihood Impact Mitigation
Library API changes (pre-release) Medium Medium Pin version, monitor releases
SimplyPrint rate limiting Low Low Use development mode, dedicated test account
Python dependency conflicts Low Low Use isolated virtual environment
WebSocket protocol changes Low High Monitor library updates

Comparison with Alternatives

Alternative 1: Physical Printer Only

Aspect Physical Virtual
Hardware cost \(200-\)2000+ $0
Maintenance High None
Test repeatability Low High
CI/CD compatible No Yes
Edge case testing Limited Unlimited
Development speed Slow Fast

Alternative 2: Mock SimplyPrint API

Aspect Mock API Virtual Printer
Implementation effort High (reverse-engineer API) Low (use library)
Accuracy Approximation Exact protocol
Maintenance burden High Low (library maintained)
Integration testing Partial Full end-to-end

Alternative 3: SimplyPrint Sandbox (if available)

SimplyPrint does not currently provide a documented sandbox or staging environment. The virtual printer approach using their official library is the recommended testing strategy.


Implementation Roadmap

Phase 1: Basic Setup (Day 1)

  1. Create tools/virtual-printer directory
  2. Set up Python environment with uv
  3. Install simplyprint-ws-client
  4. Run VirtualClient example
  5. Register virtual printer in SimplyPrint test account

Phase 2: Custom Emulator (Days 2-3)

  1. Fork/extend VirtualClient for Forma3D needs
  2. Add configurable test scenarios
  3. Implement logging and telemetry
  4. Document setup and usage

Phase 3: CI/CD Integration (Days 4-5)

  1. Create Docker container for virtual printer
  2. Add to CI/CD pipeline
  3. Implement automated test scenarios
  4. Add health checks and monitoring

Phase 4: Advanced Scenarios (Ongoing)

  1. Multi-printer farm simulation
  2. Error injection and recovery testing
  3. Performance/load testing
  4. Long-running stability tests

Conclusion

Building a SimplyPrint emulator is highly feasible and can be accomplished with minimal effort by leveraging the official simplyprint-ws-client Python library. The existing VirtualClient example provides a complete, working implementation that:

  • Connects to SimplyPrint's production servers
  • Simulates realistic printer behavior
  • Supports the full range of printer operations
  • Can be registered as a real printer in SimplyPrint
  1. Create a dedicated SimplyPrint test account for virtual printer testing
  2. Set up the virtual printer environment in tools/virtual-printer
  3. Register 1-2 virtual printers for initial testing
  4. Document the setup process for team members
  5. Integrate into CI/CD for automated testing

References


Appendix A: Complete VirtualClient Example

The following is the complete VirtualClient implementation from the official library, demonstrating all features of a virtual printer emulator:

import asyncio
import math
import random
from typing import Optional

from simplyprint_ws_client import (
    PrinterConfig,
    DefaultClient,
    GcodeDemandData,
    PrinterStatus,
    FileDemandData,
    FileProgressStateEnum,
)


def expt_smooth(target, actual, alpha, dt) -> float:
    """Exponential smoothing for realistic temperature/progress transitions"""
    return actual + (target - actual) * (1.0 - math.exp(-alpha * dt))


class VirtualConfig(PrinterConfig):
    """Custom configuration for virtual printer"""
    pass


class VirtualClient(DefaultClient[VirtualConfig]):
    """Virtual 3D printer emulator for testing"""

    job_progress_alpha: float = 0.1
    pending_job: Optional[FileDemandData] = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.printer.firmware.name = "Virtual Printer Firmware"
        self.printer.firmware.version = "1.0.0"
        self.printer.set_info("Virtual Printer", "0.0.1")
        self.printer.tool_count = 1

    async def init(self):
        """Initialize printer to default state"""
        self.printer.bed.temperature.actual = 20.0
        self.printer.bed.temperature.target = 0.0
        self.printer.tool0.temperature.actual = 20.0
        self.printer.tool0.temperature.target = 0.0
        self.printer.status = PrinterStatus.OPERATIONAL

    async def tick(self, _):
        """Called periodically to update printer state"""
        await self.send_ping()

        # Simulate temperature changes
        if self.printer.bed.temperature.target:
            self.printer.bed.temperature.actual = expt_smooth(
                self.printer.bed.temperature.target,
                self.printer.bed.temperature.actual,
                1, 0.1,
            )
        else:
            self.printer.bed.temperature.actual = 20.0

        if self.printer.tool0.temperature.target:
            self.printer.tool0.temperature.actual = expt_smooth(
                self.printer.tool0.temperature.target,
                self.printer.tool0.temperature.actual,
                1, 0.1,
            )
        else:
            self.printer.tool0.temperature.actual = 20.0

        # Simulate print progress
        if self.printer.status == PrinterStatus.PRINTING:
            self.printer.job_info.progress = expt_smooth(
                100.0, self.printer.job_info.progress,
                self.job_progress_alpha, 0.1,
            )

            if round(self.printer.job_info.progress) >= 100.0:
                self.printer.job_info.finished = True
                self.printer.status = PrinterStatus.OPERATIONAL
                self.printer.bed.temperature.target = 0.0
                self.printer.tool0.temperature.target = 0.0

    async def on_file(self, data: FileDemandData):
        """Handle file download request"""
        self.printer.status = PrinterStatus.DOWNLOADING
        self.printer.file_progress.state = FileProgressStateEnum.DOWNLOADING

        # Simulate download
        while self.printer.file_progress.percent < 100.0:
            self.printer.file_progress.percent += 10
            await asyncio.sleep(0.1)

        self.pending_job = data
        self.printer.file_progress.state = FileProgressStateEnum.READY

        if data.auto_start:
            await self.on_start_print(data)
        else:
            self.printer.status = PrinterStatus.OPERATIONAL

    async def on_start_print(self, _):
        """Start a print job"""
        self.pending_job = None
        self.printer.status = PrinterStatus.PRINTING
        self.printer.job_info.started = True
        self.printer.job_info.progress = 0.0
        self.printer.bed.temperature.target = 60.0
        self.printer.tool0.temperature.target = 225.0

    async def on_cancel(self, _):
        """Cancel current print job"""
        self.printer.status = PrinterStatus.CANCELLING
        self.printer.job_info.cancelled = True
        await asyncio.sleep(2)
        self.printer.status = PrinterStatus.OPERATIONAL
        self.printer.bed.temperature.target = 0.0
        self.printer.tool0.temperature.target = 0.0

Appendix B: Quick Start Script

Save this as run_virtual_printer.py:

#!/usr/bin/env python3
"""
Quick start script for running a virtual printer emulator.

Usage:
    pip install simplyprint-ws-client
    python run_virtual_printer.py
"""

from simplyprint_ws_client import (
    ClientApp,
    ConfigManagerType,
    ClientSettings,
    PrinterConfig,
    DefaultClient,
    PrinterStatus,
)
from simplyprint_ws_client.shared.logging import setup_logging


class VirtualPrinter(DefaultClient[PrinterConfig]):
    """Minimal virtual printer implementation"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.printer.set_info("Forma3D Virtual Printer", "1.0.0")
        self.printer.firmware.name = "Virtual Firmware"
        self.printer.firmware.version = "1.0.0"
        self.printer.tool_count = 1

    async def init(self):
        self.printer.status = PrinterStatus.OPERATIONAL
        self.printer.bed.temperature.actual = 20.0
        self.printer.tool0.temperature.actual = 20.0
        print("Virtual printer initialized and OPERATIONAL")

    async def on_connected(self):
        print(f"Connected! Setup code: {self.config.short_id}")
        print("Add this printer at: https://simplyprint.io/setup-guide")


if __name__ == "__main__":
    settings = ClientSettings(
        name="Forma3D-VirtualPrinter",
        client_factory=VirtualPrinter,
        config_factory=PrinterConfig,
        allow_setup=True,
        config_manager_t=ConfigManagerType.JSON,
        development=True,
    )

    setup_logging(settings)
    app = ClientApp(settings)

    # Load or create config
    configs = app.config_manager.get_all()
    config = configs[0] if configs else PrinterConfig.get_new()

    app.add(config)
    print("Starting virtual printer...")
    app.run_blocking()