Media Generation

Why fight Photoshop or Adobe Acrobat when you’ve already mastered HTML, CSS, and JS? Using core web technologies, BrowserCat helps you transform web pages into interactive PDFs, high-quality images, and even videos.

Here’s just a few examples of what you can do with BrowserCat.

  • PDFs
    • Branded invoices and receipts
    • Custom white papers and reports
    • Certificates, awards, and diplomas
    • Up-to-date presentation charts
  • Images
    • Templated blog headers
    • Dynamic social media images
    • Responsive infographic exports
  • Videos
    • Auto-updated video tutorials
    • Social media stories and reels
    • Personalized marketing campaigns

Preparing your content

There are two ways to generate media with a headless browser:

  1. Provide a URL to a live web page.
  2. Supply your own HTML, CSS, and JS.

Both methods are powerful and flexible, but as with all things, there are some trade-offs you should keep in mind…

Source URL

If you’re already using a frontend framework with the styles you want and the data you need, you’ll probably find it easier to create a new web page to use as the source for generating media.

If you choose this method, you’ll want to watch out for the following issues:

  • Private Access: Your source URL should not be included in your sitemap, nor should it be accessible to users. The easiest way around this is to require a secret authentication header.
  • User Authentication: If your source URL requires access to user data, you’ll want to pass along your user’s session token with the request to ensure that only the right people can trigger the generation of new media.

That said, it’s quite simple to generate media from a source URL. Here’s an example:

import * as pw from 'playwright';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pw.chromium.connect(bcatUrl, {
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.setExtraHTTPHeaders({
  // If you want to keep your source URL private
  'Private-Access-Key': '<YOUR_PRIVATE_ACCESS_KEY>',
  // If your source URL requires user authentication
  'Authorization': 'Bearer <YOUR_USER_SESSION_TOKEN>',
});
await page.goto('<YOUR_SOURCE_URL>'); 

const pdf = await page.pdf();
const image = await page.screenshot();

await browser.close();
import pp from 'puppeteer';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pp.connect({
  browserWSEndpoint: bcatUrl,
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.setExtraHTTPHeaders({
  // If you want to keep your source URL private
  'Private-Access-Key': '<YOUR_PRIVATE_ACCESS_KEY>',
  // If your source URL requires user authentication
  'Authorization': 'Bearer <YOUR_USER_SESSION_TOKEN>',
});
await page.goto('<YOUR_SOURCE_URL>'); 

const pdf = await page.pdf();
const image = await page.screenshot();

await browser.close();

Custom HTML, CSS, and JS

You won’t always have a web platform available when generating custom media. In these cases, you can use BrowserCat to render custom HTML, CSS, and JS.

The strength of this method is that you don’t need a website to generate media. You can run your media generation code on a local machine, in a serverless function, or within an API endpoint.

The downside is that you’ll have to decide on styles, templating syntax, and data access methods.

Nevertheless, it’s really easy to get going:

import * as pw from 'playwright';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pw.chromium.connect(bcatUrl, {
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.locator('html').evaluate((el, html) => { 
  return el.innerHTML = html;
}, `
  <h1>Hello, ${Math.random()}!</h1>
  <style>h1 {color: red}</style>
`);

const pdf = await page.pdf();
const image = await page.screenshot();

await browser.close();
import pp from 'puppeteer';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pp.connect({
  browserWSEndpoint: bcatUrl,
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.locator('html').evaluate((el, html) => { 
  return el.innerHTML = html;
}, `
  <h1>Hello, ${Math.random()}!</h1>
  <style>h1 {color: red}</style>
`);

const pdf = await page.pdf();
const image = await page.screenshot();

await browser.close();

In the above example, we supply a simple HTML/CSS string. However, you can easily plug-in your favorite frontend frameworks, whether they generate static assets (like Handlebars) or dynamic page content (like React).

PDF generation

Normal programmatic PDF makers are unweildy and output heavy image-based documents. But by using a headless browser with BrowserCat, your PDFs will be interactive and light by default.

The following example uses some of our favorite options for making really beautiful PDFs:

import * as pw from 'playwright';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pw.chromium.connect(bcatUrl, {
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.goto('<YOUR_SOURCE_URL>');

const pdf = await page.pdf({
  path: `test-${Date.now()}.pdf`,
  format: 'letter',
  tagged: true,
  margin: {
    top: '1in', 
    right: '1in', 
    bottom: '1in', 
    left: '1in',
  },
});

await browser.close();
import pp from 'puppeteer';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pp.connect({
  browserWSEndpoint: bcatUrl,
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.goto('<YOUR_SOURCE_URL>');

const pdf = await page.pdf({
  path: `test-${Date.now()}.pdf`,
  format: 'letter',
  tagged: true,
  margin: {
    top: '1in', 
    right: '1in', 
    bottom: '1in', 
    left: '1in',
  },
});

await browser.close();

Let’s explore the above properties:

  • path: The path to save the PDF to. If you don’t provide a path, the PDF will be returned as a buffer.
  • format: The size of the PDF. You can use standard paper sizes like ‘letter’, ‘A4’, ‘legal’, etc.
  • tagged: Adds accessibility metadata.
  • margin: The margin around the PDF. You can use any CSS units.

CSS Styles for PDFs

CSS provides a wide range of styles for managing page breaks, page flow, and nearly every aspect of control that Microsoft Office can offer. You can leverage these properties to ensure your PDFs look great, every time.

Here’s a starter to get you going:

h1 {
  /* Break page before h1s */
  page-break-before: always;
}

h1, h2, h3, h4, h5, h6 {
  /* Avoid wrapping headings across pages/columns */
  page-break-inside: avoid;
  /* Keep headings with their content */
  page-break-after: avoid;
}

table, img, svg, ol, ul, dl {
  /* Avoid page breaks across pages, but not columns */
  page-break-inside: avoid-page;
}

p {
  /* Ensure at least two lines before page break */
  orphans: 2;
  /* Ensure at least two lines after page break */
  widows: 2;
}

Image generation

The web is a visual media, and having a strong brand is key to standing out. With BrowserCat, you can generate high-quality images from your web pages, ensuring that your brand is consistent across all platforms.

Here’s an example of how to generate images from a web page:

import * as pw from 'playwright';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pw.chromium.connect(bcatUrl, {
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.goto('<YOUR_SOURCE_URL>');

await page.setViewportSize({
  width: 1200, 
  height: 630,
});
const image = await page.screenshot({
  path: `test-${Date.now()}.png`,
});

await browser.close();
import pp from 'puppeteer';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pp.connect({
  browserWSEndpoint: bcatUrl,
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.goto('<YOUR_SOURCE_URL>');

await page.setViewportSize({
  width: 1200, 
  height: 630,
});
const image = await page.screenshot({
  path: `test-${Date.now()}.png`,
});

await browser.close();

In the above example, we set the viewport size to 1200x630 pixels. Then we generated the image, and saved it to a local file.

Responsive images

One of the most annoying parts about generating social media images is that you need to recreate the image at multiple aspect ratios.

But when you’ve got CSS at your disposal, you can easily create just one template to serve all purposes.

Here’s an example:

const sizes = [
  {width: 1000, height: 1500, name: 'pinterest'},
  {width: 1080, height: 1080, name: 'instagram'},
  {width: 1104, height: 736,  name: 'linkedin' },
  {width: 1200, height: 630,  name: 'facebook' },
];

for (const {width, height, name} of sizes) {
  await page.setViewportSize({width, height});
  const pdf = await page.screenshot({
    path: `test-${name}.png`,
  });
}
@media (min-width: 1000px) {
  /* pinterest */
}

@media (min-width: 1080px) {
  /* instagram */
}

@media (min-width: 1104px) {
  /* linkedin */
}

@media (min-width: 1200px) {
  /* facebook */
}

Video generation

Wouldn’t it be great if you could generate video tutorials that stood up-to-date with your web app? Or if you could generate beautiful social media video tutorials without having to learn After Effects?

Turns out, you can! With BrowserCat, you can generate videos from your web pages with just a few lines of code.

import * as pw from 'playwright';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pw.chromium.connect(bcatUrl, {
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const context = await browser.newContext({
  recordVideo: {dir: process.cwd()},
});

const page = await browser.newPage();
await page.goto('<YOUR_SOURCE_URL>');

const cast = await page.video();
await cast.saveAs(`test-${Date.now()}.webm`);

// do stuff here - animate, interact, etc.

await page.close(); // video records page lifecycle

await browser.close();
import pp from 'puppeteer';

const bcatUrl = `wss://api.browsercat.com/connect`;
const browser = await pp.connect({
  browserWSEndpoint: bcatUrl,
  headers: {'Api-Key': '<YOUR_API_KEY>'},
});

const page = await browser.newPage();
await page.goto('<YOUR_SOURCE_URL>');

const cast = await page.screencast({
  path: `test-${Date.now()}.webm`,
});

// do stuff here - animate, interact, etc.

await cast.stop();

await browser.close();