Carlo
Carlo is a Node.js framework designed to create hybrid desktop applications by rendering Node.js data structures and UIs using a locally installed Google Chrome browser instance. It establishes communication between Node.js and the browser via the Puppeteer project, offering a remote call infrastructure for seamless interoperability. Unlike Electron or NW.js, Carlo does not bundle Chromium, relying instead on the user's existing Chrome installation. This approach can lead to smaller application sizes and leverage an up-to-date browser. The project, currently at version 0.9.46, was last updated in June 2019, and its GitHub repository under `GoogleChromeLabs` shows no recent activity, indicating it is no longer actively maintained. Key differentiators included the ability to bundle the application into a single executable using `pkg`, exposing Node.js capabilities to a web frontend, and leveraging the web stack for dynamic visualization of Node.js app states.
Common errors
-
Error: Failed to launch the browser process! No browser found at /path/to/chrome. Please download a browser at https://chromium.woolyss.com/ or set 'executablePath' to a browser in 'carlo.launch'.
cause Carlo could not locate an installed Google Chrome or Chromium browser on the system.fixInstall Google Chrome or Chromium on your system. If Chrome is installed in a non-standard location, you might need to specify its executable path in the `carlo.launch()` options, for example: `carlo.launch({ executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' })` (macOS). -
TypeError: Cannot read property 'launch' of undefined
cause The `carlo` module was not correctly imported or required, or the Node.js environment did not properly load it.fixEnsure `const carlo = require('carlo');` is at the top of your script and that `carlo` is properly installed via `npm install carlo`. Verify your Node.js version meets the `engines` requirement (>=7.6.0). -
ReferenceError: <exposedFunctionName> is not defined at <anonymous>
cause A function exposed from Node.js via `app.exposeFunction` was called from the browser before it was fully registered, or a typo exists in the function name.fixEnsure `await app.exposeFunction('functionName', ...)` completes before any browser-side code attempts to call `functionName()`. Double-check that the function name in `exposeFunction` matches the name used in the browser JavaScript exactly.
Warnings
- breaking Carlo is an abandoned project and has not been updated since June 2019. It may not be compatible with newer versions of Node.js, Puppeteer, or Google Chrome. Using it in production environments is highly discouraged due to potential security vulnerabilities, lack of maintenance, and compatibility issues.
- gotcha Carlo relies on a *locally installed* Google Chrome or Chromium browser. If Chrome is not found on the system where the application is run, Carlo will fail to launch with an error message. It does not bundle its own browser like Electron.
- gotcha The `puppeteer-core` dependency in Carlo `0.9.46` is pinned to `~1.12.0`. This old version of Puppeteer may not be compatible with very recent versions of Google Chrome, potentially leading to connection issues or unexpected behavior when Carlo tries to control a modern browser. The 'Chrome Stable channel, versions 70.*' are explicitly mentioned as supported.
- gotcha Carlo's API methods like `launch()`, `exposeFunction()`, `load()`, and `serveFolder()` return Promises. Failing to `await` these promises can lead to race conditions where subsequent operations try to interact with uninitialized browser contexts or functions that haven't been exposed yet, resulting in runtime errors.
Install
-
npm install carlo -
yarn add carlo -
pnpm add carlo
Imports
- carlo
const carlo = require('carlo');import carlo from 'carlo'; // (Not officially supported, but may work with bundlers)
- carlo.launch
const app = carlo.launch(); // Missing 'await'
const app = await carlo.launch();
- app.exposeFunction
app.exposeFunction('funcName', (arg) => { /* ... */ }); // Missing 'await' is a common mistake that can lead to race conditions.await app.exposeFunction('funcName', (arg) => { /* ... */ });
Quickstart
const carlo = require('carlo');
const path = require('path');
(async () => {
// Launch the browser. Carlo requires a locally installed Chrome/Chromium.
const app = await carlo.launch({
args: ['--disable-extensions', '--start-maximized'],
width: 800,
height: 600
});
// Terminate Node.js process on app window closing.
app.on('exit', () => process.exit());
app.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
// Serve static web files from the 'app' directory.
// Make sure to create a simple `index.html` in a folder named `app`
// or serve from `__dirname` if your html is alongside your JS file.
app.serveFolder(path.join(__dirname, 'app'));
// Expose a Node.js function 'getSystemInfo' to the web environment.
// This function will be callable from the browser-side JavaScript.
await app.exposeFunction('getSystemInfo', async () => {
return {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
envVars: Object.keys(process.env).sort()
};
});
// Navigate to the main page of your app.
await app.load('index.html');
console.log('Carlo app launched. Check the new Chrome window.');
})();
// A minimal app/index.html to be served by Carlo
// (Save this in a subfolder named 'app' next to your main JS file)
/*
<!DOCTYPE html>
<html>
<head>
<title>Carlo System Info</title>
<style>
body { font-family: sans-serif; padding: 20px; }
pre { background-color: #f4f4f4; padding: 10px; border-radius: 4px; }
</style>
</head>
<body>
<h1>System Information</h1>
<pre id="info-display"></pre>
<script>
async function displaySystemInfo() {
const info = await getSystemInfo(); // Call Node.js function
document.getElementById('info-display').textContent = JSON.stringify(info, null, 2);
}
window.onload = displaySystemInfo;
</script>
</body>
</html>
*/