mkchromecast: streaming desktop audio to Cast and Sonos from a Python script
Cast macOS and Linux Audio/Video to your Google Cast and Sonos Devices
At a glance
- What is it?
- A long lived command line tool that captures system audio on macOS or Linux and pipes it to Google Cast and Sonos speakers, with latency and sample rate as the knobs you actually control.
- Who is it for?
- mkchromecast remains the most direct answer to the specific problem of playing whatever your computer is playing on a Cast device, and it does that without a companion service or an account. The trade is explicit latency and a dependency list, since audio capture, re-encoding, and device discovery are all done locally and the README itself warns that lag between starting playback and hearing it can reach eight seconds with some backends.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 23 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the tool actually does
mkchromecast captures the audio your computer is playing and sends it to a Google Cast device or a Sonos speaker, over your local network, with no cloud service in the middle. It is written for Python 3 and does the capture and streaming work through one of three backends: `node.js`, `parec` on Linux, or `ffmpeg`.
That covers audio and video. Video files can be cast as well, and since 0.3.8 there is screencasting support and support for casting YouTube URLs through `yt-dlp` across all the sites that tool handles.
The defaults are worth knowing because they explain most of the behaviour people report. By default it streams with `node.js`, or `parec` on Linux, using MP3 at a 44100 Hz sample rate and a 192k average bitrate. Both are adjustable with `--sample-rate` and `-b`. The README gives the reason plainly: it is useful to change them when your wireless router is not very powerful, or when you would rather not degrade sound quality.
Multi-room group playback and 24-bit/96kHz high resolution audio are both supported, and there is a system tray menu. A tray menu is explicitly labelled beta and needs PyQt5.
Installing: four routes that are not equivalent
The README documents four install paths, and picking the wrong one is the most common source of confusion.
There are prebuilt binaries. On macOS that is a standalone application you drag into your `/Applications` folder, downloaded from the releases page as a disk image, and it also requires BlackHole installed separately. The Homebrew route for that same binary is:
brew install --cask mkchromecastOn Linux there are distribution packages, with the Debian build listed on packages.debian.org and an Ubuntu equivalent on packages.ubuntu.com. Installing from source is also supported, and it is the route that gets you a recent working tree.
The dependency lists differ meaningfully by platform, which is worth reading before you start. The macOS requirements for the `node.js` backend are Python 3, pychromecast, psutil, mutagen, and BlackHole, with PyQt5 optional for the tray. Switching to the ffmpeg backend adds ffmpeg and yt-dlp. The Linux list is longer and includes Pulseaudio and Pavucontrol, plus flask, vorbis-tools, sox, lame, flac, and faac, with ffmpeg, PyQt5, yt-dlp and soco marked optional.
The declared Python requirements are correspondingly small:
pip3 install socoSonos support is a single optional module, and installing it is all that is required to cast to Sonos speakers as well as Cast devices.
The latency problem, stated by the project itself
The README does not hide the central weakness. Audio is captured from the system, re-encoded, and pushed to a device over a network, and it warns that the lag between playing a song and hearing it may be up to 8 seconds for certain backends.
That number is the honest upper bound, and it is worth understanding where it comes from. There is buffering on the capture side, since the tool reads a live stream rather than a file. There is buffering at the encoding stage. And there is buffering inside the Cast device itself, which queues ahead so playback does not stutter. Change the sample rate and bitrate downward and you trade quality for a smaller buffer, which is exactly what the README suggests when your router is the bottleneck.
The release notes show the tuning work that went into this area. In 0.3.8 the chunk size changed from 1024 to 64, two more variables were added that considerably decrease the delay, and the ffmpeg commands for the PulseAudio path were reworked. Signal handling was later corrected using Python's `signal` module.
Practical takeaway: if you need low latency for anything, test with the `node.js` backend first since it is the default and the most heavily exercised path, and treat the `ffmpeg` backend as the option to reach for when you need a specific codec or format rather than the one to reach for when you need speed.
Flags worth knowing, from the release history
Much of the flag surface is documented in release notes rather than in one table, so it is worth knowing the ones that came from real problems.
`--reconnect` was renamed to `--hijack`, which is a good example of the tool doing something more interesting than its name suggests: it lets mkchromecast take over an existing Cast session rather than starting a new one. A `tries` flag was added to limit connection attempts, closing a long-standing issue where the tool kept retrying indefinitely when a device was unreachable. A `command` flag sets a custom ffmpeg or avconv binary, and a custom server port can be configured for the ffmpeg side.
Two fixes in that same release point at format handling. An error with the message about width not being divisible by two, which happens with odd dimensions like 853x480, was closed, and the `segment_time` flag was fixed. Both are the sort of thing that only matters when you are casting an oddly sized video, at which point they matter completely.
Other additions across the releases: Opus codec support, screencasting, and correct handling for Sonos devices that was throwing a `TypeError` from a print statement.
Where the signals disagree about how alive this is
The release tags and the commit history tell different stories, and both are worth weighing.
The newest tag is 0.3.8.1, published on 2017-12-24, whose entire change list is a fix for a bug when no devices were found. Before that, 0.3.8 on 2017-12-23 carried the substantive work. There are no releases in the eight years since, even though the last push was on 2026-09-14 and the repository is not archived. So development continues on the default branch without new tags, and anything installed from a release URL is roughly eight years behind the current tree. This is the single most actionable fact for anyone installing: prefer the source route unless you specifically want the tagged version.
The README also opens, above the project description, with a request for help, stating the author has not had much time recently to take care of the project and pointing at an issue asking for contributors. That is a maintainer telling you the maintenance burden is real, which is more useful than an absence of commits would be.
The license is a third signal with a wrinkle. GitHub's license detection reports `NOASSERTION` for this repository, while the README badge reads MIT and `setup.py` carries the classifier `License :: OSI Approved :: MIT License`. The MIT intent is clear from two independent places; the automated field simply failed to parse a license it could not identify. If licensing matters for your use, confirm against the `LICENSE` file rather than the metadata field.
Scale: 2,350 stars, 149 forks, and 219 open issues. That issue count against the star count is the profile of a popular tool whose user base is larger than its maintainer capacity.
The repository layout and the platform split
The tree shows two projects sharing one repository, which explains a lot about the release history and the packaging complexity.
The Python application lives in `mkchromecast/`, with `start_tray.py` and `notifier/` handling the desktop tray and notifications. There is a separate `nodejs/` directory containing `package.json`, `package-lock.json`, and `html5-video-streamer.js`, which is the streaming backend that is the default on every platform. `setup.py` confirms the arrangement by listing `nodejs/` among the data files it installs, alongside `bin/`, `notifier/`, the `.desktop` file, and the man page.
Packaging is per platform and handled by the Makefile, whose own comments describe the steps for the macOS app: patch the tray and debug flags, build with py2app, copy the Qt plugins, and run `macdeployqt`. Those Qt plugin paths in the setup.py usage notes point at a specific Cellar version, which is a small sign of how old this packaging path is.
There is also an `archive/` directory, a `changelog.md`, a `man/` directory, and a `tests/` directory. The `bin/` directory holds the platform executables, `audiodevice` among them, which is the macOS device switching helper BlackHole-style workflows depend on.
Two things are clearly documented and one is not. Requirements, install routes, and the wiki are all well covered, including separate wiki pages for ALSA capture and an FAQ. What is not settled anywhere is the audio capture path on modern Linux desktops, where PulseAudio has been widely displaced by PipeWire. The requirements list names Pulseaudio and Pavucontrol explicitly and offers ALSA as an alternative for people who do not want Pulse, but it says nothing about PipeWire, so on a current distribution you may be the first person to work out that path.
Editorial conclusion
mkchromecast remains the most direct answer to the specific problem of playing whatever your computer is playing on a Cast device, and it does that without a companion service or an account. The trade is explicit latency and a dependency list, since audio capture, re-encoding, and device discovery are all done locally and the README itself warns that lag between starting playback and hearing it can reach eight seconds with some backends. Install path matters more than it does for most tools, because the PyPI package, the Debian and Ubuntu packages, the macOS disk image, and the Homebrew cask are not equivalent. Two signals should shape your expectations: the newest tagged release is from December 2017 even though commits are still landing, and the README opens by asking for help. Use it if you want this one function, and check the wiki before you depend on it.
Frequently asked questions
Does Chromecast still work in 2026?
Cast devices still receive audio and video, and mkchromecast uses pychromecast to discover and drive them over the local network. The practical constraint is that Cast and Sonos only accept specific audio formats, so system audio is re-encoded rather than sent untouched, which is where the added latency comes from.
Why can't I cast to Chromecast anymore?
For this tool specifically, the usual causes are a missing audio capture backend and a device that cannot be discovered. On macOS the audio device must be BlackHole, which the README lists as a requirement. On Linux the backend must be present, whether PulseAudio with `parec`, ffmpeg, or ALSA. The 0.3.8.1 release fixed one case of devices not being found at all.
Is there an open-source Chromecast receiver available?
mkchromecast is the sending side, not a receiver, so it does not answer that. It talks to Cast devices using pychromecast over your local network, which is also how it reaches Sonos speakers once the optional `soco` module is installed.
How do I cast an MKV file to Chromecast?
Cast it as a video file rather than as system audio, which means mkchromecast hands the format to ffmpeg instead of capturing a stream. MKV is not a format Cast devices accept directly, so it depends on the ffmpeg backend being installed and on the source being reachable over HTTP, since the device fetches the media itself rather than receiving it pushed.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/muammar-mkchromecast)