The golden age of DOS computing produced software that defined a generation of computer users. From groundbreaking demos to productivity applications, this software represents an important chapter in computing history. DosKit provides a modern foundation for experiencing this legacy directly in web browsers, leveraging WebAssembly to run DOS binaries with remarkable fidelity.
The Preservation Imperative
DOS software faces an existential threat. As original hardware fails and operating systems evolve, the ability to run these programs diminishes. Browser-based emulation offers a compelling solution: instant access without installation, cross-platform compatibility, and the permanence of web standards.
DosKit builds on js-dos, a WebAssembly port of DOSBox, to provide a robust runtime environment. The architecture abstracts the complexity of emulation setup while exposing configuration options for advanced users.

WebAssembly: The Enabling Technology
WebAssembly makes browser-based DOS emulation practical by providing near-native execution speed:
async function initializeDosKit(containerElement, programUrl) {
const bundle = await Dos(containerElement);
const instance = await bundle.run(programUrl);
return {
instance,
sendKey: (key) => instance.sendKeyEvent(key, true),
setSpeed: (cycles) => instance.setConfig({ cycles })
};
}
The compiled DOSBox core executes at speeds sufficient for even demanding DOS software, including action games and complex demos.
Cross-Platform Consistency
One of DosKit’s primary goals is consistent behavior across platforms:
const platformConfig = {
mobile: {
touchControls: true,
virtualKeyboard: true,
audioContext: 'user-gesture-required'
},
desktop: {
touchControls: false,
fullscreenSupport: true,
keyboardCapture: true
}
};
function detectPlatform() {
const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
return isMobile ? platformConfig.mobile : platformConfig.desktop;
}
Mobile devices receive touch controls and virtual keyboards, while desktop browsers get full keyboard capture and enhanced fullscreen support.
Audio Handling Challenges
Browser audio policies require careful handling. Modern browsers block autoplay, requiring user interaction before audio can begin:
class AudioManager {
constructor() {
this.context = null;
this.initialized = false;
}
async initialize() {
if (this.initialized) return;
this.context = new AudioContext();
if (this.context.state === 'suspended') {
await this.context.resume();
}
this.initialized = true;
}
}
// Initialize on first user interaction
document.addEventListener('click', () => {
audioManager.initialize();
}, { once: true });
This pattern ensures audio works reliably while respecting browser security policies.
File System Abstraction
DOS programs expect a filesystem. DosKit provides virtual filesystem support:
async function mountFilesystem(instance, files) {
for (const [path, content] of Object.entries(files)) {
await instance.fs.writeFile(path, content);
}
}
// Example: Mount a configuration file
await mountFilesystem(dosInstance, {
'/CONFIG.SYS': 'FILES=40\nBUFFERS=25',
'/AUTOEXEC.BAT': '@ECHO OFF\nPATH C:\\;C:\\DOS'
});
This abstraction enables loading programs from URLs, IndexedDB, or user uploads while presenting a familiar DOS environment.
Performance Tuning
DOS software varies dramatically in resource requirements. DosKit provides configuration options:
const performanceProfiles = {
'8086': { cycles: 300, type: 'real' },
'286': { cycles: 3000, type: 'real' },
'386': { cycles: 8000, type: 'real' },
'486': { cycles: 25000, type: 'real' },
'max': { cycles: 'max', type: 'auto' }
};
function applyPerformanceProfile(instance, profile) {
const config = performanceProfiles[profile];
instance.setConfig({
cycles: config.cycles,
cycleType: config.type
});
}
Cycle-accurate emulation ensures software runs at authentic speeds, important for games with timing-dependent mechanics.

Touch Controls for Mobile
Mobile support requires virtual input devices:
class VirtualJoystick {
constructor(container) {
this.element = document.createElement('div');
this.element.className = 'virtual-joystick';
container.appendChild(this.element);
this.bindTouchEvents();
}
bindTouchEvents() {
this.element.addEventListener('touchmove', (e) => {
const touch = e.touches[0];
const rect = this.element.getBoundingClientRect();
const x = (touch.clientX - rect.left) / rect.width;
const y = (touch.clientY - rect.top) / rect.height;
this.emitDirection(x, y);
});
}
emitDirection(x, y) {
// Convert position to arrow key presses
if (x < 0.3) this.sendKey('ArrowLeft');
if (x > 0.7) this.sendKey('ArrowRight');
if (y < 0.3) this.sendKey('ArrowUp');
if (y > 0.7) this.sendKey('ArrowDown');
}
}
These controls make DOS software accessible on devices that never existed during the DOS era.
State Preservation
Save states enable users to pause and resume sessions:
async function saveState(instance) {
const state = await instance.saveState();
const blob = new Blob([state], { type: 'application/octet-stream' });
// Store in IndexedDB for persistence
await stateStorage.save('last-session', blob);
}
async function loadState(instance) {
const blob = await stateStorage.load('last-session');
if (blob) {
const state = await blob.arrayBuffer();
await instance.loadState(state);
}
}
This feature transforms ephemeral browser sessions into persistent experiences.
Conclusion
DosKit demonstrates that computing history need not be locked away in museums or abandoned to bit rot. WebAssembly provides the performance necessary for faithful emulation, while modern web APIs enable rich, cross-platform experiences. The result is immediate, barrier-free access to software that shaped the computing landscape.
Experience DOS classics at doskit.net or explore the source at github.com/cameronrye/doskit.