#!/usr/bin/env node /** * OBS Virtual Camera with Noise Source * * This script connects to OBS via websocket and: * 1. Creates a scene with a noise/random pattern source * 2. Starts the virtual camera * * Prerequisites: * - OBS Studio 28+ (has obs-websocket built-in) * - Enable WebSocket Server in OBS: Tools -> WebSocket Server Settings * - Set a password or disable authentication for local use * * Usage: node obs-noise-camera.mjs [start|stop] [--password=YOUR_PASSWORD] */ import OBSWebSocket from 'obs-websocket-js'; const obs = new OBSWebSocket(); const SCENE_NAME = 'NoiseCamera'; const SOURCE_NAME = 'RandomNoise'; async function connect(password) { const url = 'ws://127.0.0.1:4455'; try { const config = password ? { rpcVersion: 1, password } : { rpcVersion: 1 }; await obs.connect(url, password); console.log('Connected to OBS WebSocket'); return true; } catch (err) { if (err.message?.includes('Authentication')) { console.error('Authentication required. Use --password=YOUR_PASSWORD'); console.error('Or disable authentication in OBS: Tools -> WebSocket Server Settings'); } else { console.error('Failed to connect to OBS:', err.message); console.error('Make sure OBS is running and WebSocket Server is enabled'); } return false; } } async function createNoiseScene() { // Check if scene exists const { scenes } = await obs.call('GetSceneList'); const sceneExists = scenes.some(s => s.sceneName === SCENE_NAME); if (!sceneExists) { console.log(`Creating scene: ${SCENE_NAME}`); await obs.call('CreateScene', { sceneName: SCENE_NAME }); } // Switch to the scene await obs.call('SetCurrentProgramScene', { sceneName: SCENE_NAME }); // Check if source exists in scene const { sceneItems } = await obs.call('GetSceneItemList', { sceneName: SCENE_NAME }); const sourceExists = sceneItems.some(item => item.sourceName === SOURCE_NAME); if (!sourceExists) { console.log(`Creating noise source: ${SOURCE_NAME}`); // Create a color source with shader/noise effect // We'll use a color source and apply noise filter, or use browser source with noise await obs.call('CreateInput', { sceneName: SCENE_NAME, inputName: SOURCE_NAME, inputKind: 'color_source_v3', inputSettings: { color: 0xFF808080, // Gray base width: 1920, height: 1080 } }); // Add a noise/static filter using the Shader filter if available // Or we can use a different approach - let's try adding the "Noise Displacement" filter try { await obs.call('CreateSourceFilter', { sourceName: SOURCE_NAME, filterName: 'ShaderNoise', filterKind: 'streamfx-filter-shader', filterSettings: { 'Shader.Type': 0, // File } }); } catch (e) { // StreamFX might not be installed, try built-in approach console.log('Note: For best results, install StreamFX plugin for shader-based noise'); } } console.log(`Scene "${SCENE_NAME}" ready`); } async function createBrowserNoiseSource() { // Alternative: Create a browser source with animated noise const { scenes } = await obs.call('GetSceneList'); const sceneExists = scenes.some(s => s.sceneName === SCENE_NAME); if (!sceneExists) { console.log(`Creating scene: ${SCENE_NAME}`); await obs.call('CreateScene', { sceneName: SCENE_NAME }); } await obs.call('SetCurrentProgramScene', { sceneName: SCENE_NAME }); const { sceneItems } = await obs.call('GetSceneItemList', { sceneName: SCENE_NAME }); const sourceExists = sceneItems.some(item => item.sourceName === SOURCE_NAME); if (!sourceExists) { console.log(`Creating browser noise source: ${SOURCE_NAME}`); // HTML/JS that generates animated noise const noiseHtml = `data:text/html,
`; await obs.call('CreateInput', { sceneName: SCENE_NAME, inputName: SOURCE_NAME, inputKind: 'browser_source', inputSettings: { url: noiseHtml, width: 1920, height: 1080, fps: 30, reroute_audio: false } }); } console.log(`Browser noise source "${SOURCE_NAME}" ready`); } async function startVirtualCamera() { const { outputActive } = await obs.call('GetVirtualCamStatus'); if (!outputActive) { console.log('Starting virtual camera...'); await obs.call('StartVirtualCam'); console.log('Virtual camera started'); } else { console.log('Virtual camera already running'); } } async function stopVirtualCamera() { const { outputActive } = await obs.call('GetVirtualCamStatus'); if (outputActive) { console.log('Stopping virtual camera...'); await obs.call('StopVirtualCam'); console.log('Virtual camera stopped'); } else { console.log('Virtual camera not running'); } } async function getStatus() { const { outputActive } = await obs.call('GetVirtualCamStatus'); const { currentProgramSceneName } = await obs.call('GetCurrentProgramScene'); console.log(`Virtual Camera: ${outputActive ? 'RUNNING' : 'STOPPED'}`); console.log(`Current Scene: ${currentProgramSceneName}`); } async function main() { const args = process.argv.slice(2); const command = args.find(a => !a.startsWith('--')) || 'start'; const passwordArg = args.find(a => a.startsWith('--password=')); const password = passwordArg ? passwordArg.split('=')[1] : undefined; if (!await connect(password)) { process.exit(1); } try { switch (command) { case 'start': await createBrowserNoiseSource(); await startVirtualCamera(); await getStatus(); break; case 'stop': await stopVirtualCamera(); break; case 'status': await getStatus(); break; default: console.log('Usage: node obs-noise-camera.mjs [start|stop|status] [--password=PASS]'); } } catch (err) { console.error('Error:', err.message); } finally { obs.disconnect(); } } main();