The LiveCodes SDK provides a powerful event system that allows you to watch for various playground events and react to changes in real-time.
The watch Method
The primary way to listen to events is through the watch method:
const watcher = playground . watch ( eventName , callback );
// Later, remove the watcher
watcher . remove ();
Signature
type WatchFn = (
event : 'load' | 'ready' | 'code' | 'console' | 'tests' | 'destroy' ,
callback : ( data ?: any ) => void
) => { remove : () => void }
Available Events
load
Called when the playground first loads.
playground . watch ( 'load' , () => {
console . log ( 'Playground has loaded' );
});
Callback Parameters : None
When it fires : Once when the playground iframe loads for the first time.
Example Use Case : Track analytics, show loading complete message
ready
Called when a new project is loaded and the playground is ready to run.
playground . watch ( 'ready' , ({ config }) => {
console . log ( 'Playground is ready with config:' , config );
});
Callback Parameters :
config (Config): The current configuration object
When it fires :
After initial load
When importing a new project
When setConfig completes
Example Use Case : Auto-run code when ready, update UI state
playground . watch ( 'ready' , async ({ config }) => {
console . log ( 'Loaded project:' , config . title );
// Auto-run the code
await playground . run ();
});
code
Called when the playground content changes.
playground . watch ( 'code' , ({ code , config }) => {
console . log ( 'Code changed!' );
console . log ( 'Script content:' , code . script . content );
console . log ( 'Config:' , config );
});
Callback Parameters :
code (Code ): Current code in all editors
config (Config): Current configuration
When it fires : When content changes in:
Code editors
Editor languages
CSS processors
External resources
Custom settings
Project info
Project title
Test code
Example Use Case : Save to localStorage, sync with server, show unsaved indicator
let hasUnsavedChanges = false ;
playground . watch ( 'code' , ({ code , config }) => {
hasUnsavedChanges = true ;
// Save to localStorage
localStorage . setItem ( 'draft' , JSON . stringify ({ code , config }));
});
console
Called when console output occurs in the result page.
playground . watch ( 'console' , ({ method , args }) => {
console [ method ]( ... args );
});
Callback Parameters :
method (string): Console method name ('log', 'info', 'warn', 'error', etc.)
args (any[]): Arguments passed to the console method
When it fires : When code in the result page calls console methods
Example Use Case : Mirror console output, log monitoring, debugging
const logs = [];
playground . watch ( 'console' , ({ method , args }) => {
logs . push ({ method , args , timestamp: Date . now () });
// Mirror to parent console
console [ method ]( '[Playground]' , ... args );
});
tests
Called when tests run and produce results.
playground . watch ( 'tests' , ({ results , error }) => {
results . forEach (( result ) => {
console . log ( 'Test:' , result . testPath . join ( ' > ' ));
console . log ( 'Status:' , result . status );
console . log ( 'Duration:' , result . duration , 'ms' );
if ( result . errors . length > 0 ) {
console . error ( 'Errors:' , result . errors );
}
});
});
Callback Parameters :
results (TestResult[] ): Array of test results
error (string, optional): Error message if tests failed to run
When it fires : After tests run (manually or via auto-test)
Example Use Case : Display test results, CI/CD integration, test reporting
playground . watch ( 'tests' , ({ results , error }) => {
if ( error ) {
console . error ( 'Test error:' , error );
return ;
}
const passed = results . filter ( r => r . status === 'pass' ). length ;
const failed = results . filter ( r => r . status === 'fail' ). length ;
console . log ( `Tests: ${ passed } passed, ${ failed } failed` );
});
destroy
Called when the playground is destroyed.
playground . watch ( 'destroy' , () => {
console . log ( 'Playground destroyed' );
});
Callback Parameters : None
When it fires : When playground.destroy() is called
Example Use Case : Cleanup, analytics, state management
playground . watch ( 'destroy' , () => {
console . log ( 'Cleaning up playground resources' );
// Perform cleanup
});
Removing Watchers
Always remove watchers when you no longer need them to prevent memory leaks:
const watcher = playground . watch ( 'code' , ({ code }) => {
console . log ( 'Code changed' );
});
// Later...
watcher . remove ();
Multiple Watchers
const watchers = [];
watchers . push (
playground . watch ( 'code' , handleCodeChange ),
playground . watch ( 'console' , handleConsole ),
playground . watch ( 'tests' , handleTests )
);
// Remove all watchers
watchers . forEach ( w => w . remove ());
Complete Examples
React Example
import { useEffect , useState } from 'react' ;
import { createPlayground } from 'livecodes' ;
function PlaygroundWithEvents () {
const [ playground , setPlayground ] = useState ( null );
const [ logs , setLogs ] = useState ([]);
useEffect (() => {
createPlayground ( '#container' , { template: 'javascript' })
. then ( setPlayground );
}, []);
useEffect (() => {
if ( ! playground ) return ;
const watchers = [
playground . watch ( 'ready' , () => {
console . log ( 'Ready!' );
}),
playground . watch ( 'console' , ({ method , args }) => {
setLogs ( prev => [ ... prev , { method , args }]);
}),
];
return () => watchers . forEach ( w => w . remove ());
}, [ playground ]);
return (
< div >
< div id = "container" />
< div >
{ logs . map (( log , i ) => (
< div key = { i } > { log . method } : { JSON . stringify ( log . args ) } </ div >
)) }
</ div >
</ div >
);
}
Vue Example
< script setup >
import { ref , onMounted , onUnmounted } from 'vue' ;
import { createPlayground } from 'livecodes' ;
const container = ref ( null );
const playground = ref ( null );
const logs = ref ([]);
const watchers = [];
onMounted ( async () => {
playground . value = await createPlayground ( container . value , {
template: 'javascript' ,
});
watchers . push (
playground . value . watch ( 'ready' , () => {
console . log ( 'Ready!' );
}),
playground . value . watch ( 'console' , ({ method , args }) => {
logs . value . push ({ method , args });
})
);
});
onUnmounted (() => {
watchers . forEach ( w => w . remove ());
playground . value ?. destroy ();
});
</ script >
< template >
< div >
< div ref = "container" ></ div >
< div v-for = " ( log , i ) in logs " : key = " i " >
{{ log . method }}: {{ JSON . stringify ( log . args ) }}
</ div >
</ div >
</ template >
Svelte Example
< script >
import { onMount , onDestroy } from 'svelte' ;
import { createPlayground } from 'livecodes' ;
let container ;
let playground ;
let logs = [];
let watchers = [];
onMount ( async () => {
playground = await createPlayground ( container , {
template: 'javascript' ,
});
watchers . push (
playground . watch ( 'ready' , () => {
console . log ( 'Ready!' );
}),
playground . watch ( 'console' , ({ method , args }) => {
logs = [ ... logs , { method , args }];
})
);
});
onDestroy (() => {
watchers . forEach ( w => w . remove ());
playground ?. destroy ();
});
</ script >
< div >
< div bind : this = { container } ></ div >
< div >
{# each logs as log , i ( i )}
< div > { log . method } : { JSON . stringify ( log . args ) } </ div >
{/ each }
</ div >
</ div >
TestResult Interface
The test results object structure:
interface TestResult {
duration : number ; // Test duration in milliseconds
errors : string []; // Array of error messages
status : 'pass' | 'fail' | 'skip' ; // Test status
testPath : string []; // Path to the test (e.g., ['describe block', 'test name'])
}
Example:
playground . watch ( 'tests' , ({ results }) => {
results . forEach ( result => {
if ( result . status === 'fail' ) {
console . error ( `❌ ${ result . testPath . join ( ' > ' ) } ` );
result . errors . forEach ( err => console . error ( ' ' , err ));
} else if ( result . status === 'pass' ) {
console . log ( `✅ ${ result . testPath . join ( ' > ' ) } ( ${ result . duration } ms)` );
}
});
});
Deprecated: onChange
The onChange method is deprecated. Use watch('code', callback) instead.
// ❌ Deprecated
playground . onChange (({ code , config }) => {
console . log ( 'Code changed' );
});
// ✅ Use this instead
playground . watch ( 'code' , ({ code , config }) => {
console . log ( 'Code changed' );
});
Event Flow
Typical event sequence:
load - Playground iframe loads
ready - Project is loaded and ready
code - User modifies code (can fire multiple times)
console - Code runs and produces console output
tests - Tests run (if triggered)
destroy - Playground is destroyed
playground . watch ( 'load' , () => console . log ( '1. Load' ));
playground . watch ( 'ready' , () => console . log ( '2. Ready' ));
playground . watch ( 'code' , () => console . log ( '3. Code changed' ));
playground . watch ( 'console' , () => console . log ( '4. Console output' ));
playground . watch ( 'tests' , () => console . log ( '5. Tests ran' ));
playground . watch ( 'destroy' , () => console . log ( '6. Destroyed' ));
Best Practices
Always remove watchers : Prevent memory leaks by calling remove() when done
Handle errors : Wrap callbacks in try-catch for production code
Debounce expensive operations : For code events, consider debouncing
let debounceTimer ;
playground . watch ( 'code' , ({ code }) => {
clearTimeout ( debounceTimer );
debounceTimer = setTimeout (() => {
saveToServer ( code );
}, 1000 );
});
Check playground state : Ensure playground isn’t destroyed before calling methods
Use TypeScript : Get type safety for event data
import type { Code , Config , TestResult } from 'livecodes' ;
playground . watch ( 'code' , ({ code , config } : { code : Code ; config : Config }) => {
// Fully typed!
});
Next Steps
Methods Learn about all available playground methods
Types Browse complete TypeScript type definitions
API Reference Review the complete API reference
React Component Use events with the React component