Python Playground — Run Python in Your Browser

A Python editor and interpreter that runs entirely in this tab. Standard library, standard input, real tracebacks — and a one-time runtime download you can see before it starts.

Python is not loaded yet. The first run downloads about 13 MB from jsDelivr once, then the runtime is cached locally; your code stays in this tab.

Dependencies

Add package names, then install them without running your code. Status shows whether each package is ready to import.

No packages selected

    Pyodide-built package files are cached in this browser after first use; other pure-Python packages are fetched from PyPI with micropip. Native extensions without a WebAssembly build cannot load. requests is patched to use browser fetch, so CORS still applies. Only install packages you trust — their code runs in the sandbox and can make network requests.

    CORS-enabled APIs; baidu.com is shown as a failure case
    Outputstdout and stderr
     

    The interpreter runs in a Worker inside a sandboxed iframe. Stop discards it, so the next run starts from a clean namespace.

    What this tool does

    This page runs a real CPython interpreter in your browser. Type a snippet, put any values it should read from input() into the Standard input box, and press Run. Output and errors appear beside the editor. The interpreter is CPython 3.14.2 compiled to WebAssembly by Pyodide 314.0.7, not a JavaScript imitation of Python, so the behaviour you see matches the Python documentation for that release.

    This is a script runner rather than a REPL: a bare expression such as 2 + 2 is evaluated but does not print a value. Use print() when you want to see a result.

    The editor highlights Python syntax, completes builtins and common standard-library calls such as statistics.mean and math.sqrt, and shows a short signature when you hover a known function. It loads after the first paint, so the page and the interpreter controls stay light while the editor arrives.

    It is built for the small, useful things: checking a regular expression, testing a date calculation, trying an algorithm, explaining a slice, or confirming what an exception actually says. It is not a replacement for a local development environment, and it is not intended for large data-science workflows.

    How the runtime works

    Pyodide is a port of CPython to WebAssembly. The first run downloads the loader, the WebAssembly module and the standard-library archive — roughly 13 MB — from jsDelivr, then starts the interpreter in a Worker inside a sandboxed iframe on this page. The files are ordinary static assets served by a CDN; no Python process runs on our server, and there is no server-side execution path at all. After the first load, the page keeps the pinned core files in the browser’s Cache Storage, so a later visit normally starts without downloading them again; clearing site data or using a private window removes that cache.

    The explicit second click before that download is deliberate. A 13 MB fetch is a real cost on a slow or metered connection, and hiding it behind a “Run” button that appears to do nothing for several seconds is worse than stating it. The status line tells you when the runtime is loading, ready or running, and Stop is available while it loads or executes.

    Installing dependencies

    The Dependencies panel is deliberately separate from the code editor. Add package names and press Install dependencies; the page loads Python if necessary, resolves the packages and reports each one without executing your snippet. This is useful when you want to see whether an install works before writing the code that uses it, and it avoids a failed package turning into a misleading traceback on the first Run.

    A row moves from queued to installing and then to ready or failed. Ready means the package was installed and a Python import check found its module; installed* means installation succeeded but the import name could not be verified. The panel also names the source (Pyodide or PyPI), the version, the import modules and the dependencies it knows about. Package installation is an operation with a lot of lines, so the auto-installed dependency rows and the installation log are collapsed by default; expand either disclosure when you want the detail. The log records installation start, versions, import checks and failures, and it opens automatically on an error. A package that is installed but cannot be imported is reported rather than silently treated as successful.

    Run also installs missing packages. If the Packages box is empty but the snippet imports requests, numpy or another package in the Pyodide lock file, the import is mapped to its distribution automatically. Dependencies are resolved by Pyodide for bundled packages and by micropip for PyPI wheels. Pyodide’s own package files are cached in the browser after the first fetch, so a later Run reuses those files instead of downloading them again. Everything still lives in the sandbox runtime: Stop resets installed packages along with the interpreter, while the downloaded core and package files stay in the browser cache.

    Calling an API with requests

    A browser page cannot make an ordinary cross-origin HTTP request and read the response unless the other server opts in with CORS. The Python runner patches requests through pyodide-http so the library uses the browser’s fetch machinery; it does not — and cannot — remove that browser rule. A call to a site such as baidu.com therefore ends in a NetworkError even after the URL has the correct https:// scheme. The same applies to urllib, urllib3 and Pyodide’s pyfetch.

    The HTTP request examples selector above the editor loads runnable calls to APIs that do send the right headers: JSONPlaceholder for a simple GET and .json(), Open-Meteo for query parameters, httpbin for a JSON POST, DummyJSON for another GET, urllib3 for a different client, and pyfetch for an async browser-native call. Select an example and press Run; if requests is not installed yet, the import is detected and the package is installed automatically. The CORS failure example is deliberately included so the difference between a missing package and a blocked response is visible.

    To reach a site that does not allow CORS, use a local Python environment or put a small proxy you control in front of it. A public CORS proxy is a third party that can read the request and response, so do not send secrets through one you do not control. This page has no server-side proxy, and adding one would break its no-upload model. The browser-execution guide explains the boundary in more detail.

    Standard input and input()

    Browsers do not give a WebAssembly process a terminal, so this page supplies one. Values in the Standard input box are read one line at a time by input(), in order. The prompt is written and flushed before the read, so name = input("Name: ") shows “Name: ” and then waits for the next line. When the box is exhausted, input() raises EOFError, exactly as a Python script reading an empty file would.

    The input text is passed to Python as data, never interpolated into the source of your snippet. A line containing quotes, backslashes or Python syntax is just a string; it cannot break out of the reader and become code.

    State, Stop and fresh starts

    Repeated runs share one interpreter, so definitions persist: if the first run defines def tax(x): ..., the next run can call tax(10) without redefining it. The standard-input reader is rebuilt from the box on each run, so lines are not consumed across runs. That is useful for iterative work and surprising if you expected each run to be a fresh process; the status line shows when the runtime is loaded.

    Stop throws the interpreter away. It is the reliable reset when a loop will not end, when an import left a module in a strange state, or when you want a clean namespace. The next run starts a fresh interpreter while reusing the cached core files, and starts with no user globals. Stopping also clears the current computation, so output produced before the stop remains visible but the program is no longer running.

    What is available

    AreaStatus in this playgroundNotes
    Core languageAvailableCPython 3.14.2 syntax, exceptions, classes, generators and async/await
    Standard libraryMostly availablePure-Python modules work; modules tied to a native OS or GUI may not
    Third-party packagesLimitedPyodide-built packages and pure-Python wheels via micropip; native extensions may lack a wasm build
    FilesIn-memory onlyThe sandbox has a temporary filesystem; files vanish on Stop or reload
    NetworkSubject to CORSrequests is patched to use browser fetch; urllib and pyfetch obey CORS too
    Threads and processesLimitedThere is no native process model; browser isolates are not OS threads

    Privacy and the sandbox

    The Python iframe is sandboxed with allow-scripts only and without allow-same-origin. It has an opaque origin, so code running there cannot read this site’s cookies, localStorage or DOM, and it cannot navigate the page that contains it. Results come back only as messages over postMessage. Your source code is passed through that same channel and is never sent to this site’s server; there is no endpoint that could receive it.

    The runtime and any Pyodide-built packages are downloaded from jsDelivr; packages installed with micropip are fetched from PyPI. Those requests reveal your IP address and user agent to the relevant CDN, as any package download would, but they do not contain your code or your data. If your own Python calls a URL, that request comes from your browser and is governed by that service’s CORS policy and privacy policy, not by this page.

    Working within the limits

    WebAssembly is fast for a browser, but it is not native CPython. Startup takes a moment, large integers and tight numeric loops are slower, and memory is bounded by the tab. The standard library is broad but not complete: modules that expect a real kernel, a GUI display or a compiled extension may fail at import. A ModuleNotFoundError or an ImportError in the output is usually the environment telling you which side of that line the module is on, not a bug in the page.

    For a quick exception check, this is faster than opening a terminal. For a real program with dependencies, a test suite, files that must persist and a debugger, use a local environment. The playground is the scratchpad, not the workshop.

    Frequently asked questions

    Is my Python code uploaded anywhere?

    No. The editor and the interpreter both run in this browser tab. Your code is sent by postMessage to a sandboxed iframe on the same page, which runs Pyodide in a Worker; it is not sent to this site, to jsDelivr or to any other server. The runtime files themselves are downloaded from jsDelivr, so that request reveals your IP address and browser to the CDN in the ordinary way, but it does not contain your code.

    Which Python version is this?

    CPython 3.14.2, compiled to WebAssembly by Pyodide 314.0.7. The version is pinned in the page so an update cannot silently change language behaviour under an existing snippet. It is a real CPython build, not a JavaScript re-implementation, so the syntax and standard-library behaviour match the CPython documentation for that release.

    Does it work like a Python REPL?

    No — it runs a script. An expression such as 2 + 2 is evaluated but not printed; use print(2 + 2) to see a value. This is the same distinction as running python file.py versus typing into the interactive prompt. Variables persist between runs, but the output panel shows only what you print or what Python writes to stderr.

    Why do I have to press Run twice the first time?

    The first execution downloads the Python runtime — about 13 MB of WebAssembly, standard-library code and loader — from jsDelivr. The first click explains the download; the second click starts it. This makes the cost visible instead of surprising you on a metered connection. The files are stored in the browser’s Cache Storage after the first load, so later visits normally reuse them without another download; clearing site data or using a private window removes that cache.

    Can I install third-party packages?

    Yes. Add package names in the Dependencies panel, separated by commas, and press Install dependencies. Packages built for Pyodide — requests, numpy, pandas and many others — load from the pinned runtime CDN; other pure-Python packages are installed from PyPI with micropip. Version specifiers are not accepted in the box, and packages whose native extensions have no WebAssembly build cannot load. Packages live in the sandbox’s memory and disappear when you press Stop or reload.

    How do I know an installation finished and the package is usable?

    Each package gets a row in the Dependencies panel: queued, installing, ready, installed* or failed. Ready means the install completed and an import check found the module; installed* means the distribution was installed but its import name could not be verified (often a data-only or namespace package). The row also shows the source, version, import name and known dependencies. The dependency log keeps the same events as text.

    Are dependencies installed automatically?

    Yes. Pyodide resolves the dependencies of its built-in packages, and micropip resolves the runtime dependencies of a PyPI wheel. The panel lists them as dependency rows when Pyodide knows them. If your code imports a package and the Packages box is empty, Run detects the top-level import and installs the matching Pyodide package automatically.

    Does requests work?

    Yes. The runner loads pyodide-http automatically when you import requests, urllib or urllib3, and patches requests to use the browser’s fetch API instead of raw sockets. The browser’s CORS rules still apply: an API that sends Access-Control-Allow-Origin can be fetched, while an ordinary website that does not allow cross-origin requests will be blocked even though requests itself is installed. That is a browser security boundary, not a missing package. The HTTP request examples selector shows working CORS-enabled calls.

    Why does https://www.baidu.com still fail after fixing the scheme?

    Because baidu.com does not send Access-Control-Allow-Origin, so the browser refuses to expose the response to this page. Installing requests cannot change that; CORS is enforced by the browser. Use an API that allows cross-origin requests (JSONPlaceholder, Open-Meteo, httpbin and DummyJSON are in the examples selector), or run the code in a local Python environment, or send it through a proxy you control. This site does not provide a CORS proxy.

    Which APIs can I call from this playground?

    The HTTP request examples selector includes GET JSON with JSONPlaceholder, query parameters with Open-Meteo, POST JSON with httpbin and GET JSON with DummyJSON. They are chosen because they send CORS headers. The examples show status codes, JSON parsing and query parameters; select one, press Run, and requests installs automatically if needed.

    Why does requests.get("www.baidu.com") fail with MissingSchema?

    A URL passed to requests needs a scheme. "www.baidu.com" has no scheme, so requests raises MissingSchema before any network request is made; write "https://www.baidu.com" instead. The runner prints this hint before the traceback and summarizes it in the status line. A scheme is necessary but not sufficient: the target must also allow browser CORS, so an ordinary site such as baidu.com may still be blocked even after the URL is fixed.

    Which Python HTTP clients work in the browser?

    requests and urllib3 are patched by pyodide-http to use fetch and are the best-supported choices; urllib.request is patched too. Pyodide’s own pyfetch is browser-native and shown as an async example. Clients that expect raw sockets or their own HTTP stack, such as httpx or aiohttp, are not guaranteed to work against a browser page and may fail even when the package imports. Use a local Python environment when you need their full transport features.

    Where do the package downloads come from?

    Pyodide-built packages come from the pinned jsDelivr runtime; packages not bundled with Pyodide are fetched from PyPI by micropip. The page caches the package files that Pyodide’s lock file names, so a later visit or Run normally reads them from Cache Storage instead of the CDN. PyPI wheels resolved by micropip still use the PyPI fetch path. Those requests reveal your IP address and user agent, as any package download would, but your code and data are not uploaded. The site has no server-side package proxy or storage.

    How do I stop an infinite loop?

    Press Stop. The interpreter runs in a Worker inside a sandboxed iframe, so Stop can discard the iframe and terminate the Worker even when Python itself is stuck in a tight loop; the editor stays responsive. Stopping resets the interpreter: variables defined in the current session are lost, and the next run starts a fresh interpreter. The downloaded runtime files stay in the browser’s Cache Storage, so they are not fetched again.

    Does input() work?

    Yes. Put one value per line in the Standard input box. The page installs a small Python prelude that replaces the builtin input() with a reader over that text, flushing the prompt before each read. If the box runs out of lines, input() raises EOFError, which is the same behaviour as reading from an empty stream in a normal Python process.

    Do variables persist between runs?

    Yes, until you press Stop or reload the page. Repeated runs share the same interpreter globals, so a function or value defined in one run is still there in the next. The standard-input reader is rebuilt from the box on every run, so input lines do not silently carry over. Stop discards the entire interpreter, which is the reliable way to get a clean namespace.

    Can my code read files or call the network?

    The browser gives the sandbox an in-memory filesystem, not your disk; files disappear when the interpreter is stopped. Network access through urllib or Pyodide’s pyfetch is subject to the same CORS rules as any browser request, so many endpoints will refuse it. The code cannot read this site’s cookies, localStorage or DOM because the iframe is sandboxed without allow-same-origin.

    Why is it slower than Python on my laptop?

    The interpreter is WebAssembly running inside a browser sandbox, and the first run also pays for compiling and initialising the runtime. Small snippets feel instant once loaded; CPU-heavy loops, large integer work and big data structures are noticeably slower than native CPython. For timing-sensitive work, measure the same snippet locally.

    What happens to imports from the standard library?

    Most pure-Python and many built-in modules are available: math, statistics, json, re, datetime, collections, itertools, functools, decimal, fractions, random and pathlib are typical examples. Modules that need a real operating system, threads in the native sense, GUI toolkits or compiled third-party extensions are either unavailable or limited by the WebAssembly environment. If an import fails, the traceback names the module.

    Sources

    • Pyodide documentation — loading Pyodide, the JavaScript API, stdout/stdin handling and the runtime files.
    • Python 3.14 documentation — language reference and standard library.
    • MDN Web Docs — iframe sandbox and the same-origin policy.
    • Pyodide 314.0.7 release notes and the pinned pyodide-lock.json (Python 3.14.2, wasm32).
    • micropip documentation — installing PyPI wheels into a Pyodide session.
    • pyodide-http — patching requests, urllib and urllib3 to use the browser’s fetch implementation.

    Everything runs in your browser — nothing you type is uploaded.