(Backup domain: frogconvert.netlify.app)
Truly universal online file converter.
This project is a fork of "Convert to it!" (original repo here). All credit for the core file conversion engine and logic goes to the original developer. This fork is a reimagining of the UI/UX with quality-of-life improvements.
Compared to the original Convert to it!, frogConvert focuses on frontend improvements:
- Redesigned Modern UI/UX: A completely fresh, visually appealing look with dedicated modules, light/dark theme toggles, and a refined file format selection interface.
- Enhanced Mobile Experience: Fully responsive layout with a hamburger menu, fixed file name overflowing, and optimized padding and alignments for smaller screens.
- Improved Feedback & Animations: Introduced smooth animations and clear conversion progress indicators (e.g., "x out of y converting").
- File Management & Uploads: Introduced a new file management feature and set limits on maximum file uploads to prevent crashes and improve stability.
- Dynamic Capabilities: Smart detection of device RAM capabilities to prevent the browser tab from freezing or crashing during heavy workloads.
This section is adapted from the original README.
Many online file conversion tools are boring and insecure. They only allow conversion between two formats in the same medium (images to images, videos to videos, etc.), and they require that you upload your files to some server.
This is not just terrible for privacy, it's also incredibly lame. What if you really need to convert an AVI video to a PDF document? Try to find an online tool for that, I dare you.
frogConvert runs entirely in your browser. You're almost guaranteed to get an output - perhaps not always the one you expected, but it'll try its best to not leave you hanging.
For a semi-technical overview of the original tool, check out this video: https://youtu.be/btUbcsTbVA8
- Upload your file — Drag and drop a file onto the upload zone, or click it to browse. You can upload multiple files at once (up to the device limit).
- Auto-detection — frogConvert automatically detects your file's format and selects the matching input type. The category tab (Image, Audio, Video, etc.) switches to match.
- Pick an output format — Click the format selector button to open the format picker. Browse by category tabs, or use the search bar to find a specific format. Click a format to select it.
- Convert — Hit the Convert button. A progress indicator shows how many files have been processed.
- Download — Once conversion finishes, your converted file downloads automatically.
- Any-to-any — frogConvert can chain multiple conversion tools together to reach formats that no single tool supports directly. Want to turn a WAV into a PDF? Go for it.
- Privacy first — Everything runs in your browser. No files are ever uploaded to a server.
- Theme toggle — Switch between light and dark mode with the theme button in the top bar.
- Mode toggle — Switch between "Simple" and "All" mode to control how many output formats are shown.
- Multiple files — When you upload more than one file, use the file manager to review, add, remove, or replace individual files.
- Performance — frogConvert detects your device's available RAM and adjusts limits to prevent crashes on lower-end hardware.
The deployment steps below are adapted from the original README, updated for this fork's repository URL.
- Clone this repository WITH SUBMODULES. You can use
git clone --recursive https://github.com/ogfrench/frogConvertfor that. Omitting submodules will leave you missing a few dependencies. - Install Bun.
- Run
bun installto install dependencies. - Run
bunx viteto start the development server.
The following steps are optional, but recommended for performance:
When you first open the page, it'll take a while to generate the list of supported formats for each tool. If you open the console, you'll see it complaining a bunch about missing caches.
After this is done (indicated by a Built initial format list message in the console), use printSupportedFormatCache() to get a JSON string with the cache data. You can then save this string to cache.json to skip that loading screen on startup.
Docker compose files live in the docker/ directory, so run compose with -f from the repository root:
docker compose -f docker/docker-compose.yml up -dAlternatively download the docker-compose.yml separately and start it by executing docker compose up -d in the same directory.
This runs the container on http://localhost:8080/convert/.
Use the override file to build the image locally:
docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml up --build -dThe first Docker build is expected to be slow because Chromium and related system packages are installed in the build stage (needed for puppeteer in scripts/buildCache.js). Later builds are usually much faster due to Docker layer caching.
The contributing guidelines below are adapted from the original README. The handler interface and code structure are unchanged from the original project.
The best way to contribute is by adding support for new file formats (duh). Here's how that works:
Each "tool" used for conversion has to be normalized to a standard form - effectively a "wrapper" that abstracts away the internal processes. These wrappers are available in src/handlers.
Below is a super barebones handler that does absolutely nothing. You can use this as a starting point for adding a new format:
// file: dummy.ts
import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts";
import CommonFormats from "src/CommonFormats.ts";
class dummyHandler implements FormatHandler {
public name: string = "dummy";
public supportedFormats?: FileFormat[];
public ready: boolean = false;
async init () {
this.supportedFormats = [
// Example PNG format, with both input and output disabled
CommonFormats.PNG.builder("png")
.markLossless()
.allowFrom(false)
.allowTo(false),
// Alternatively, if you need a custom format, define it like so:
{
name: "CompuServe Graphics Interchange Format (GIF)",
format: "gif",
extension: "gif",
mime: "image/gif",
from: false,
to: false,
internal: "gif",
category: ["image", "video"],
lossless: false
},
];
this.ready = true;
}
async doConvert (
inputFiles: FileData[],
inputFormat: FileFormat,
outputFormat: FileFormat
): Promise<FileData[]> {
const outputFiles: FileData[] = [];
return outputFiles;
}
}
export default dummyHandler;For more details on how all of these components work, refer to the doc comments in src/FormatHandler.ts. You can also take a look at existing handlers to get a more practical example.
There are a few additional things that I want to point out in particular:
- Pay attention to the naming system. If your tool is called
dummy, then the class should be calleddummyHandler, and the file should be calleddummy.ts. - The handler is responsible for setting the output file's name. This is done to allow for flexibility in rare cases where the full file name matters. Of course, in most cases, you'll only have to swap the file extension.
- The handler is also responsible for ensuring that any byte buffers that enter or exit the handler do not get mutated. If necessary, clone the buffer by wrapping it in
new Uint8Array(). - When handling MIME types, run them through normalizeMimeType first. One file can have multiple valid MIME types, which isn't great when you're trying to match them algorithmically.
- When implementing a new file format, please treat the file as the media that it represents, not the data that it contains. For example, if you were making an SVG handler, you should treat the file as an image, not as XML.
If your tool requires an external dependency (which it likely does), there are currently two well-established ways of going about this:
- If it's an
npmpackage, just install it to the project like you normally would. - If it's a Git repository, add it as a submodule to src/handlers.
Please try to avoid CDNs (Content Delivery Networks). They're really cool on paper, but they don't work well with TypeScript, and each one introduces a tiny bit of instability. For a project that leans heavily on external dependencies, those bits of instability can add up fast.
- If you need to load a WebAssembly binary (or similar), add its path to vite.config.js and target it under
/convert/wasm/. Do not link to node_modules.