Open-source project
p2r3/convert avatar
p2r3/convert

p2r3/convert: a browser-side file converter that crosses media types

Truly universal online file converter

4,068 stars391 forksTypeScriptGPL-2.0

At a glance

What is it?
Convert to it! runs every conversion inside the browser with WebAssembly tools, so files never leave the machine. It is built for cross-medium jobs like AVI to PDF, and it is harder to self-host than a plain CLI wrapper.
Who is it for?
Adopt p2r3/convert if you want a converter that runs in the browser and handles cross-medium jobs rather than a fixed format matrix. Skip it if you need a documented conversion API or a format that no handler covers, because the README does not describe either.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The cross-medium problem Convert to it! was written for

Most online converters stay inside one medium. Images go to images, videos to videos, and the tool never crosses the boundary. Convert to it! takes the opposite position. Its README asks what you do when you "really need to convert an AVI video to a PDF document" and points out that finding an online tool for that is difficult. The project's stated aim is to "just works", with the README admitting that you are "almost guaranteed to get an output", perhaps not the one you expected.

The audience follows from that. This is for people with an awkward one-off file: a video that has to become a document, a format that no mainstream converter lists. It is not aimed at pipelines that convert thousands of files on a schedule, because there is no documented batch or headless conversion interface. The README's issue policy makes the same point from the other side: it states plainly that asking for a format to be added is not a meaningful issue, and that a proposal must explain the expected conversion medium, link to existing browser-based solutions, and confirm the reference license is compatible with GPL-2.0.

How handlers, formats and the browser-side toolchain fit together

The conversion engine is a set of handlers in src/handlers. Each handler is a TypeScript class implementing FormatHandler and exposing a name, a supportedFormats array, a ready flag, an offload flag, an init method and a doConvert method. The README calls this a wrapper that "abstracts away the internal processes", and the dummy example shows the shape: formats are declared either through CommonFormats builders such as CommonFormats.PNG.builder("png").markLossless().allowFrom(false).allowTo(false), or as custom objects with name, format, extension, mime, from, to, internal, category and lossless fields.

The dependency list shows where the actual work happens. The browser loads WebAssembly builds of 7-Zip, zstd, FFmpeg, ImageMagick, a PPTX renderer and a WASI shim, among others. That is the reason the project can cross media types: an AVI can be decoded by the FFmpeg build and the result handed to another handler that emits a document. It also explains the privacy claim, since the file is processed locally rather than uploaded. The cost is weight. The README notes that the first Docker build is slow because Chromium and related system packages are installed for puppeteer in buildCache.js, and that later builds are faster through Docker layer caching.

That cache is the part worth understanding before you run anything. On first load the app generates the supported format list for each tool, and the README says the console complains about missing caches until a "Built initial format list" message appears. Calling printSupportedFormatCache() returns a JSON string you can save as cache.json to skip the loading screen. The trade-off is stated in the README itself: if changes seem not to apply, try disabling the cache.

Running Convert to it! locally with Bun and Vite

The README lists Bun and Vite as the local development path. The clone must include submodules, and the README warns that omitting them leaves you missing dependencies. Run these from the repository root.

bash
git clone --recursive https://github.com/p2r3/convert
cd convert
bun install
bunx vite

After bun install, the postinstall script in package.json runs scripts/extract-material-icons.ts, and bunx vite starts the development server. Open the page, then watch the console. Once it prints "Built initial format list", call printSupportedFormatCache() and save the returned JSON string as cache.json so later startups skip that generation step. To use the tool itself, add a file by clicking the blue box or dragging it onto the window, check the detected input format, pick an output format from the second list, and click Convert.

Docker deployment and the Chromium build cost

The compose files sit in docker/, so compose has to be pointed at them from the repository root. The README gives the prebuilt image path first.

bash
docker compose -f docker/docker-compose.yml up -d

That runs the container at http://localhost:8080/convert/. To build the image yourself instead, add the override file.

bash
docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml up --build -d

The README states that the first Docker build is expected to be slow because Chromium and related system packages are installed in the build stage for puppeteer, and that later builds are usually much faster. Budget for that first build rather than assuming something has hung. The package.json scripts mirror this: docker:build passes the current git commit as VITE_COMMIT_SHA, and npm run docker chains the build and up steps. There are also desktop scripts, including desktop:dist:win, desktop:dist:mac and desktop:dist:linux through electron-builder, so an Electron build exists, though the README's usage section only documents the web interface.

Where Convert to it! is the wrong tool

The README is direct that not every format can be supported, and that adding one can take hours. The practical consequence is that a missing format is a normal state, not a defect, and the issue policy says the maintainers already know which formats are absent. If your workload depends on a format no handler covers, the project cannot help you today, and the README offers no roadmap for when it might.

The second limitation is the interface. Everything documented is interactive: the usage steps describe clicking, dragging and selecting from lists. There is no conversion API, no command-line entry point for converting a file, and no batch mode in the README. A service that needs to convert files on demand from another program has nothing to call here.

Third, the handler example shows the cost of a crude conversion. The README argues that parsing underlying data is not sufficient, using SVG as the example: treating SVG only as raw XML and not supporting conversion to raster images would defeat the point, and it warns against "binary waterfalls". A handler that satisfies the type signature but does not model the medium can pass review and still produce useless output. The README's own framing, that you are almost guaranteed an output but not always the expected one, is the honest description of that risk.

How it differs from FFmpeg and ImageMagick directly

The obvious alternative is to run FFmpeg or ImageMagick yourself, and the comparison is not about capability. Those tools are the ones this project embeds, as WebAssembly builds in the dependency list. The difference is where they run and who wires them together. A local FFmpeg invocation keeps files on disk but requires you to know the right flags for each conversion and to chain tools by hand when the source and target media differ. Convert to it! puts that chaining in handlers and exposes it as two dropdowns in a browser tab.

The other alternative is a hosted conversion service. Those accept an upload, which is exactly what the README objects to when it says tools require that you "upload your files to some server", calling that bad for privacy. The trade here is real in both directions: the browser build downloads a large WebAssembly toolchain and generates a format cache on first load, while a hosted service pays that cost on its own hardware and returns a link. If your files are large or your network is slow, the local model shifts the cost onto the client.

Licence and maintenance cost of a GPL-2.0 handler set

The repository is GPL-2.0, and the README makes licence compatibility a condition for format proposals, asking contributors to confirm that a reference implementation's license is compatible with GPL-2.0. That matters if you plan to embed the project in a product: a copyleft licence on the codebase is a different proposition from calling a permissively licensed library, and the README does not discuss linking exceptions or commercial terms. Treat that as something to check with your own legal review rather than something the project answers.

The upgrade cost concentrates in two places. The format cache is one: it is generated from the handlers, so adding or changing a handler can invalidate a saved cache.json, and the README already notes that a stale cache makes changes appear not to apply. The build stage is the other, since Chromium and system packages are installed for puppeteer, which means base image changes can move the build. The last push to the repository was on 2026-09-21, and the latest release is a rolling release dated 2026-03-16, so there is no versioned release line to pin against. Track the master branch and regenerate the cache after handler changes.

Editorial conclusion

Adopt p2r3/convert if you want a converter that runs in the browser and handles cross-medium jobs rather than a fixed format matrix. Skip it if you need a documented conversion API or a format that no handler covers, because the README does not describe either. Before deploying, verify that a Docker build succeeds on your machine, that the cache.json you generate still matches your handler list, and that puppeteer and Chromium install cleanly in the build stage.

Frequently asked questions

How can I convert files with p2r3/convert?

Open convert.to.it, add a file by clicking the blue box or dragging it onto the window, confirm the detected input format, choose an output format from the second list, and click Convert. The README notes the output may not always be the one you expected.

How do I install p2r3/convert locally?

Clone the repository with submodules using git clone --recursive, install Bun, then run bun install and bunx vite. The README warns that omitting submodules leaves you missing dependencies.

Can I run p2r3/convert with Docker?

Yes. The compose files are in docker/, so run docker compose -f docker/docker-compose.yml up -d from the repository root, which serves the container at http://localhost:8080/convert/. Add the override file to build the image locally.

Does p2r3/convert upload my files to a server?

The README criticizes tools that require uploading files to a server and presents local processing as the point of the project. The dependency list is made up of WebAssembly builds such as FFmpeg, ImageMagick and 7-Zip, which run in the browser.

Why does p2r3/convert take a long time to load the first time?

The README states that the page first generates the list of supported formats for each tool, and that the console complains about missing caches until a "Built initial format list" message appears. You can call printSupportedFormatCache() and save the result as cache.json to skip that step.

Which file formats does p2r3/convert support?

The README does not list supported formats and says the project cannot support every file format. Formats are declared per handler in src/handlers, and the app builds its format list from those handlers on first load.

Official sources

  1. License: GPL-2.0
  2. p2r3/convert on GitHub
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/p2r3-convert.svg)](https://hysenlabs.com/projects/p2r3-convert)