npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kadal/deck-python

v0.12.0

Published

The Python renderer for Kadal Deck (@kadal/deck): Python islands on a slide, through Pyodide.

Readme

@kadal/deck-python

Build it. Rehearse it. Own the room. Kadal Deck has your back for the whole talk, from the first idea to the last applause. This package is part of building it: real Python running on a slide, so your demo runs in front of the room, not in a screenshot.

Take the tour · Docs

The Python renderer for Kadal Deck, presentations written in MDX: Python code as an island on a slide, loaded only when its slide appears.

Python islands guide · Islands in any framework · Docs · Source

npm install @kadal/deck-python

Python islands on a deck slide, through Pyodide — CPython compiled to WebAssembly.

registerLazyRenderer("python", () => import("@kadal/deck-python").then((m) => m.pythonRenderer));
# ticker.py
def deck_mount(host, props):
    return Ticker(host, props)       # with deck_update(props) and deck_destroy()
<Island
  framework="python"
  load={() => import("./ticker.py?raw").then((m) => deckPython(m.default))}
  props={{ data }}
/>

A Python island is source text

Every other component in the engine is a value the bundler produced: an options object, a compiled function, a class, a WASM module. Python has no build step and no bundle — CPython compiled to WebAssembly reads source at runtime, exactly as it does on a server. So <Island> is handed a string, ?raw is the whole pipeline, and the renderer's work begins with runPython.

The contract's IslandComponent being unknown is what makes that legal, and this is the case that justifies it. The engine never looks inside a component, so a component can be a file.

Three things with no equivalent anywhere else in the engine

1. destroy is a reserved word on this boundary. PyProxy.destroy() is the proxy's own lifetime method. A Python class with a method of that name is shadowed by it, so handle.destroy() from JavaScript quietly frees the proxy and never runs the island's teardown — the interval keeps ticking behind nineteen slides and nothing reports a thing. The agreement is therefore deck_mount, deck_update and deck_destroy. Snake_case, which is what Python wanted.

2. Proxies must be freed by hand. Garbage collection cannot cross the boundary in either direction: the JavaScript collector does not know the Python object exists, and Python's does not know JavaScript is holding it. Every proxy the renderer takes — the handle, each bound method, the namespace — is released explicitly. Forget one and the leak is in WASM linear memory, where no browser tool will show it to you. The same applies inside the island: a create_proxy callback handed to setInterval has to be destroyed when the island is.

3. Property access is not .get(). A PyProxy of a dict has .get; a proxy of a class instance does not — its methods are properties, and each read mints a new bound-method proxy. Which is why the renderer reads them once and keeps them.

One interpreter, shared

Pyodide is a six-megabyte download and a second or two of startup — the most expensive thing in this engine by two orders of magnitude. It is fetched once, lazily, from a pinned jsDelivr URL, shared by every Python island on the deck, and deliberately not torn down when the last island leaves: reclaiming the memory would make the next Python slide pay the six megabytes again, and a deck with one Python island usually has two.

Each island still runs in its own namespace dict. A shared interpreter means a shared global namespace, and two islands both defining deck_mount would silently be the same island twice.

It needs the network

Pyodide is loaded from https://cdn.jsdelivr.net/pyodide/v<pinned>/full/. The version is pinned because an interpreter that changes under a deck is a presentation that breaks on stage. A deck presented offline cannot run a Python island unless it serves Pyodide itself — pass setPyodideIndexUrl() for that.

On the showcase slide, the Python ticker reads a few seconds behind the other nine. That is not a bug: it is the download, visible.

Licence

AGPL-3.0-or-later, with a commercial licence available — the same terms as the engine. See LICENSING.md.