Dynamic Workers Playground
Try the Dynamic Workers playground ↗ to write or import code from GitHub, bundle it at runtime, execute it in a Dynamic Worker, and view real-time logs.

- Runtime bundling — Uses
@cloudflare/worker-bundler↗ to resolve npm dependencies and compile TypeScript inside a Worker - Dynamic execution — Loads bundled code into an isolated Dynamic Worker
- Caching — Reuses previously bundled Workers when the source has not changed
- Real-time output — Streams the response body, console logs, execution timing, and bundle metadata back to the client
The playground uses @cloudflare/worker-bundler ↗ to compile TypeScript, resolve npm dependencies, and produce modules the Worker Loader can execute.
Pass source files and a package.json to createWorker(), which resolves dependencies and returns bundled modules ready to load as a Dynamic Worker:
import { createWorker } from "@cloudflare/worker-bundler";
const { mainModule, modules, warnings } = await createWorker({ files: { "src/index.ts": userCode, "package.json": JSON.stringify({ dependencies: { hono: "^4.0.0" }, }), }, bundle: true, minify: false,});import { createWorker } from "@cloudflare/worker-bundler";
const { mainModule, modules, warnings } = await createWorker({ files: { "src/index.ts": userCode, "package.json": JSON.stringify({ dependencies: { hono: "^4.0.0" }, }), }, bundle: true, minify: false,});env.LOADER.load() creates a new Dynamic Worker on every call. To avoid re-bundling unchanged code, use env.LOADER.get(id, callback) instead. The runtime returns an existing Worker on a cache hit, or calls your callback to build one on a miss:
const worker = env.LOADER.get(workerId, async () => { // This callback only runs on cache miss const { mainModule, modules } = await createWorker({ files });
return { mainModule, modules, compatibilityDate: "2026-05-01", tails: [contextExports.DynamicWorkerTail({ props: { workerId } })], };});
const response = await worker.getEntrypoint().fetch(request);const worker = env.LOADER.get(workerId, async () => { // This callback only runs on cache miss const { mainModule, modules } = await createWorker({ files });
return { mainModule, modules, compatibilityDate: "2026-05-01", tails: [ contextExports.DynamicWorkerTail({ props: { workerId } }), ], };});
const response = await worker.getEntrypoint().fetch(request);In the playground, you can see this in action — run the same Dynamic Worker twice and the second request shows a cached result with 0ms cold start, since the build and load phases are skipped entirely.
When you run code in the playground, console output from the Dynamic Worker streams back to the browser in real time. Under the hood, this works through a Tail Worker pipeline:
- A Tail Worker (
DynamicWorkerTail) capturesconsole.logoutput from the Dynamic Worker. - Logs are forwarded to a
LogSessionDurable Object. - The Durable Object streams them to the client over WebSocket.
To wire this up, include the Tail Worker in the tails array when creating the Dynamic Worker:
const worker = env.LOADER.get(workerId, async () => ({ mainModule, modules, compatibilityDate: "2026-05-01", tails: [contextExports.DynamicWorkerTail({ props: { workerId } })],}));const worker = env.LOADER.get(workerId, async () => ({ mainModule, modules, compatibilityDate: "2026-05-01", tails: [contextExports.DynamicWorkerTail({ props: { workerId } })],}));For more information on how to capture and stream logs from Dynamic Workers, refer to Observability with Dynamic Workers.
Clone the repo and start the dev server:
npm installnpm run dev