ScreenshotNeo

BlogHow-to

Screenshot Webpages as JPEG in Ruby

Use Ferrum and headless Chrome to capture viewport, full-page, element, or regional webpage screenshots as JPEG files in Ruby.

By the ScreenshotNeo team1 October 20266 min read

Use Ferrum with Chrome or Chromium to render a webpage and save it as JPEG. Set format: "jpeg", provide a destination with path:, and optionally set quality: from 0 to 100.

require "ferrum"

browser = Ferrum::Browser.new
begin
  page = browser.create_page
  page.go_to("https://example.com")
  page.screenshot(path: "page.jpg", format: "jpeg", quality: 80)
ensure
  browser.quit
end

The Ruby gem does not install Chrome for you. Install Chrome or Chromium separately and make sure the binary is on PATH, or configure Ferrum with the BROWSER_PATH environment variable.

1. Install Ferrum and a browser

Add Ferrum to your project

Add the gem to your Gemfile and install dependencies:

source "https://rubygems.org"
gem "ferrum"
bundle install

Install Chrome or Chromium through your operating system. Verify that the executable is discoverable:

which google-chrome || which chromium || which chromium-browser

If the browser is installed somewhere else, point Ferrum at it with BROWSER_PATH before starting Ruby:

BROWSER_PATH=/path/to/chrome ruby screenshot.rb

Run a minimal script

Save the first example as screenshot.rb and run:

bundle exec ruby screenshot.rb

Ferrum navigates the page in headless Chrome, captures the rendered result, and writes binary JPEG data to page.jpg.

2. Choose JPEG format and quality

Ferrum accepts jpeg and jpg; jpg is normalized to JPEG. An explicit format is clearest:

page.screenshot(path: "homepage.jpeg", format: "jpeg", quality: 85)

When format is omitted, Ferrum can infer the format from a useful file extension. If neither an explicit format nor the path selects another format, PNG is the default. JPEG quality is a 0–100 setting. Higher values generally preserve more detail and produce larger files; lower values produce smaller files with more compression. Select a value based on your visual and storage requirements.

Write to a custom directory

require "fileutils"
require "ferrum"

FileUtils.mkdir_p("shots")
browser = Ferrum::Browser.new
begin
  page = browser.create_page
  page.go_to("https://example.com")
  page.screenshot(
    path: "shots/example.jpg",
    format: "jpeg",
    quality: 80
  )
ensure
  browser.quit
end

Get image data instead of writing a file

Without path:, Ferrum returns base64 data by default. This is useful when another API expects an encoded payload:

data = page.screenshot(format: "jpeg", quality: 80)
File.write("page.base64", data)

For ordinary file output, use path:; Ferrum then writes the binary image for you.

3. Capture the viewport, full page, an element, or a region

Viewport screenshot

The default captures the visible browser viewport:

page.screenshot(path: "viewport.jpg", format: "jpeg")

Full document screenshot

Use full: true to capture the full document rather than only the viewport:

page.screenshot(
  path: "full-page.jpg",
  format: "jpeg",
  quality: 80,
  full: true
)

Capture one element

Pass a CSS selector with selector::

page.screenshot(
  path: "pricing-card.jpg",
  format: "jpeg",
  selector: ".pricing-card",
  quality: 85
)

The selector must match an element that exists after the page has rendered. A missing selector causes capture to fail, so wait for the element when the page is dynamic.

Capture a rectangular area

Use area: with x, y, width, and height:

page.screenshot(
  path: "region.jpg",
  format: "jpeg",
  area: { x: 0, y: 120, width: 900, height: 600 }
)

Ferrum applies these priorities when options overlap: full: true takes precedence over selector and area; selector takes precedence over area. Choose one capture mode to avoid surprises.

4. Control the rendered page before capture

A screenshot records the browser state at capture time. Navigate first, then establish the viewport and wait for content that is loaded asynchronously. Navigation finishing does not guarantee that every image, chart, or client-side component is ready.

Set a viewport

browser = Ferrum::Browser.new(window_size: [1440, 900])

Use the viewport that matches the output you need. A responsive page can produce a different layout at mobile, tablet, and desktop widths.

Wait for a known element

Prefer a site-specific readiness condition when possible. For example, wait until the content you need exists before capturing:

page.go_to("https://example.com/dashboard")
page.at_css(".dashboard-ready")
page.screenshot(path: "dashboard.jpg", format: "jpeg", quality: 80)

If a page has no reliable readiness selector, use a deliberate delay sparingly and keep it long enough for that site’s asynchronous work. Avoid assuming one universal delay works everywhere.

Use a deterministic capture helper

require "ferrum"

url = ARGV.fetch(0, "https://example.com")
output = ARGV.fetch(1, "page.jpg")

browser = Ferrum::Browser.new(window_size: [1440, 900])
begin
  page = browser.create_page
  page.go_to(url)
  page.screenshot(path: output, format: "jpeg", quality: 80, full: true)
ensure
  browser.quit
end
bundle exec ruby screenshot.rb https://example.com example.jpg

5. A complete Ruby example with error handling

require "ferrum"

url = ARGV.fetch(0, "https://example.com")
output = ARGV.fetch(1, "page.jpg")

browser = Ferrum::Browser.new(
  window_size: [1440, 900]
)

begin
  page = browser.create_page
  page.go_to(url)

  # Replace this with a selector that proves your page is ready.
  # page.at_css("main")

  page.screenshot(
    path: output,
    format: "jpeg",
    quality: 80,
    full: true
  )

  puts "Saved #{output}"
rescue Ferrum::Error => e
  warn "Screenshot failed: #{e.message}"
  exit 1
ensure
  browser.quit
end

Run it with:

bundle exec ruby screenshot.rb https://example.com example.jpg

6. Troubleshooting

Symptom Likely cause Fix
cannot find Chrome or browser startup failure Chrome/Chromium is not installed or is not on PATH. Install a browser and verify it with which. Set BROWSER_PATH to the executable when it is in a custom location.
Output is PNG instead of JPEG No JPEG format or informative extension was supplied. Set format: "jpeg" and use a .jpg or .jpeg path.
JPEG is unexpectedly large or blurry The quality setting does not match the size and fidelity target. Adjust quality between 0 and 100 and compare representative pages.
Only the visible portion is captured Viewport capture is the default. Set full: true for the full document.
Element capture fails The selector is missing, changes after load, or matches no element. Inspect the selector and wait for a page-specific readiness element before calling screenshot.
Dynamic content is missing Capture happened before client-side rendering or lazy loading completed. Wait for a known selector or another condition that represents readiness; do not rely on navigation alone.
Wrong responsive layout The browser viewport differs from the target device. Set window_size when creating the browser and capture again.
Browser process remains after an exception The browser was not closed. Wrap capture code in begin ... ensure ... browser.quit ... end.

7. Performance, reliability, and cost considerations

  • Starting Chrome is relatively expensive compared with taking another page in an already-running browser. Reuse one browser process for a batch, while creating pages per capture.
  • Full-page images use more memory than viewport or element captures, especially on long documents. Capture only the required region when possible.
  • JPEG encoding trades file size against visual quality. Set quality deliberately instead of assuming the default fits every workload.
  • Dynamic pages are the main reliability risk. Use a readiness selector tied to the page’s actual content, and handle navigation and capture exceptions.
  • Ferrum and Chrome are local infrastructure. Your cost is the machine or container running Ruby and the browser; there is no screenshot-service request fee in this workflow.
  • For repeatable jobs, pin your Ruby dependencies, keep the browser available in the deployment image, and write outputs to a known writable directory.

8. Or skip the browser setup

ScreenshotNeo provides a hosted screenshot API, so Ruby code can request a JPEG without installing Ferrum or Chrome. See the ScreenshotNeo API documentation for the available parameters.

require "net/http"
require "uri"

uri = URI("https://api.screenshotneo.com/v1/shot")
uri.query = URI.encode_www_form(
  access_key: "YOUR_API_KEY",
  url: "https://stripe.com",
  format: "jpeg"
)

response = Net::HTTP.get_response(uri)
raise "Screenshot request failed: #{response.code} #{response.message}" unless response.is_a?(Net::HTTPSuccess)

File.binwrite("shot.jpg", response.body)

Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account and start with 1,000 screenshots a month at no charge.

9. FAQ

Does Ferrum convert a PNG into JPEG?

No conversion step is required for normal screenshots. Ask Ferrum for JPEG with format: "jpeg"; Chrome produces the requested screenshot format.

Can I use a .jpg filename without format:?

Yes. Ferrum can infer the format from a useful path extension, but setting format: "jpeg" makes the intent explicit.

What quality should I choose?

There is no universal best value. Start with a representative quality such as 80, inspect text and images, then adjust for your file-size and fidelity requirements.

Why is my full-page image very tall?

full: true captures the document’s full height. Use viewport, selector, or area capture when you need a bounded image.

Can Ferrum capture a page without Chrome installed?

No. Ferrum controls headless Chrome, so Chrome or Chromium must be installed separately and discoverable through PATH or BROWSER_PATH.