Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
Chrome DevTools Protocol

How to Screenshot a Webpage as a JPEG in Go

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use chromedp to drive a Chrome DevTools Protocol browser, then save the returned image bytes with a .jpg or .jpeg name. For a true JPEG, set a quality below 100 when using chromedp’s FullScreenshot; in the reviewed implementation, quality 100 selects PNG instead. If you need explicit encoding control, call the lower-level page.CaptureScreenshot command with FormatJpeg and a quality from 0 through 100.

Choose the capture you actually need

A browser screenshot can mean three different things:

  • Viewport: only the currently visible browser area.
  • Element: one DOM element, such as a chart or product card.
  • Full page: content extending below the viewport.

The choice affects the API call. chromedp’s FullScreenshot helper captures beyond the viewport, while ordinary screenshot tasks can target the visible page or an element. The official example shows element and full-page task structures, but writes files with .png names; a filename does not change the bytes’ encoding.

Prerequisites and project shape

You need a Go module, a Chrome/Chromium browser that supports the Chrome DevTools Protocol, and the chromedp and cdproto/page packages. The sources used here do not define a browser-version compatibility matrix or a universal browser-install procedure, so use the browser setup appropriate for your operating system and deployment image.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Initialize a module and add the packages with your normal Go dependency workflow. Keep the browser executable available to the process, and make sure the account running the program can launch it in your environment.

Capture a full webpage as JPEG with chromedp

This complete program navigates to a URL, waits for the document body, captures the full page, and writes the returned bytes to a JPEG file.

package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "github.com/chromedp/chromedp"
)

func main() {
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    ctx, cancel = context.WithTimeout(ctx, 60*time.Second)
    defer cancel()

    var image []byte
    err := chromedp.Run(ctx,
        chromedp.Navigate("https://example.com"),
        chromedp.WaitVisible("body", chromedp.ByQuery),
        chromedp.FullScreenshot(&image, 90),
    )
    if err != nil {
        panic(err)
    }

    if err := os.WriteFile("page.jpg", image, 0644); err != nil {
        panic(err)
    }
    fmt.Printf("wrote %d bytes to page.jpgn", len(image))
}

The second argument to FullScreenshot is JPEG quality in the helper’s non-100 path. In the reviewed source, quality 100 selects PNG; values other than 100 select JPEG. Therefore, do not use 100 when your output must be JPEG. The exact visual and file-size trade-off depends on the page and browser encoding; the cited sources do not establish a single best quality value.

Why the extension is not enough

os.WriteFile("page.jpg", image, ...) only chooses a name. It does not transcode data. If the helper produced PNG bytes, the file is still PNG even if it ends in .jpg. Select JPEG in the capture API, then use a matching extension.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture only the viewport

For a screenshot of what is currently visible, use the ordinary screenshot action rather than FullScreenshot:

var image []byte
err := chromedp.Run(ctx,
    chromedp.Navigate("https://example.com"),
    chromedp.WaitVisible("body", chromedp.ByQuery),
    chromedp.CaptureScreenshot(&image),
)
if err != nil {
    return err
}
if err := os.WriteFile("viewport.jpg", image, 0644); err != nil {
    return err
}

Check the helper’s encoding behavior for your dependency version before assuming this action returns JPEG. When format must be unambiguous, use the protocol-level call shown next.

Set JPEG format and quality directly with cdproto/page

The Chrome DevTools Protocol page package exposes screenshot format and JPEG quality explicitly. The protocol documents JPEG quality as 0–100; PNG is the default format unless you choose another format.

package main

import (
    "context"
    "os"
    "time"

    "github.com/chromedp/cdproto/page"
    "github.com/chromedp/chromedp"
)

func capture(ctx context.Context, url string) error {
    var image []byte
    err := chromedp.Run(ctx,
        chromedp.Navigate(url),
        chromedp.WaitVisible("body", chromedp.ByQuery),
        chromedp.ActionFunc(func(ctx context.Context) error {
            data, err := page.CaptureScreenshot().
                WithFormat(page.CaptureScreenshotFormatJpeg).
                WithQuality(90).
                Do(ctx)
            if err != nil {
                return err
            }
            image = data
            return nil
        }),
    )
    if err != nil {
        return err
    }
    return os.WriteFile("page.jpg", image, 0644)
}

func main() {
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()
    ctx, cancel = context.WithTimeout(ctx, 60*time.Second)
    defer cancel()
    if err := capture(ctx, "https://example.com"); err != nil {
        panic(err)
    }
}

This path makes the requested format visible in your code review: WithFormat(page.CaptureScreenshotFormatJpeg) requests JPEG and WithQuality(90) supplies a value in the documented range. The browser returns encoded image bytes; no second conversion step is required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture one element

The chromedp example repository demonstrates element screenshot tasks. A typical pattern is to select an element and capture its box:

var image []byte
err := chromedp.Run(ctx,
    chromedp.Navigate("https://example.com"),
    chromedp.WaitVisible(".hero", chromedp.ByQuery),
    chromedp.Screenshot(".hero", &image, chromedp.ByQuery),
)
if err != nil {
    return err
}
return os.WriteFile("hero.jpg", image, 0644)

As with viewport capture, verify the action’s format behavior in the version you use, or replace the action with a protocol call when strict JPEG output matters. If the selector matches nothing, the wait or screenshot action fails; use the selector that exists in the rendered DOM, not merely in server-side source.

Wait for the page you intend to capture

Navigation completion does not guarantee that images, fonts, charts, or client-rendered content are visible. Add a wait that represents your page’s ready state:

  • Wait for a stable selector with WaitVisible.
  • Use a delay only when the page has no reliable readiness element.
  • For application pages, wait for a chart container or a “loaded” state rather than an arbitrary short sleep.

Give the context a deadline. Without one, a stalled navigation can keep a worker occupied indefinitely. A full-page capture can also be much larger than a viewport image, so allocate and store the returned byte slice accordingly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common failures and fixes

Chrome cannot be started

Symptom: the context fails before navigation. Cause: no usable Chrome/Chromium executable, missing runtime libraries, or a restricted sandbox. Fix: install or expose a supported browser in the runtime image and follow the launch requirements for that environment. The chromedp project documentation does not promise a single setup for every operating system.

Timeout during navigation

Symptom: the context deadline expires. Fix: confirm the URL is reachable from the worker, increase the deadline for genuinely slow pages, and wait on a page-specific readiness selector rather than capturing immediately.

Blank or incomplete image

Cause: capture ran before client rendering, the selector was hidden, or content requires scrolling or interaction. Fix: wait for visible content, perform the required interaction as a chromedp task, and use FullScreenshot when below-viewport content is required.

The file says JPEG but tools identify PNG

Cause: the bytes were encoded as PNG, commonly because quality 100 selected PNG in FullScreenshot. Fix: choose a non-100 quality for that helper or explicitly set FormatJpeg with the protocol API, then save with a matching extension.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Images or fonts are missing

Cause: resources are still loading, blocked by the network, or require a later application state. Fix: wait for a selector that appears only after the resources are ready, and check the page under the same network and authentication conditions as the capture worker.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

  • Full page costs more memory: the returned byte slice and encoded image grow with page dimensions and detail.
  • Reuse browser infrastructure carefully: creating a context per job gives isolation; a long-lived browser can reduce startup work but needs cleanup and limits for stuck pages.
  • Control concurrency: too many simultaneous browser pages can exhaust CPU, memory, file descriptors, or network capacity.
  • Make outputs observable: log URL, capture type, elapsed time, byte count, and errors. Do not treat a successful navigation as proof that the image contains the intended content.
  • Quality is a trade-off: lower JPEG quality generally changes size and detail, but the supplied API documentation does not provide a benchmark or recommended value.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF, so your Go service can download the response without managing a local browser:

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
)

func main() {
    endpoint := "https://api.screenshotneo.com/v1/shot"
    q := url.Values{}
    q.Set("access_key", "YOUR_API_KEY")
    q.Set("url", "https://stripe.com")

    resp, err := http.Get(endpoint + "?" + q.Encode())
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        panic(fmt.Sprintf("ScreenshotNeo returned %s", resp.Status))
    }

    out, err := os.Create("shot.webp")
    if err != nil {
        panic(err)
    }
    defer out.Close()
    if _, err := io.Copy(out, resp.Body); err != nil {
        panic(err)
    }
}

See the ScreenshotNeo API documentation for request options and response details. Before capture, it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server includes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account to get the 1,000 monthly screenshots without a card.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which approach should you use?

Need Best fit Why
Local browser control, custom interactions, or authenticated sessions chromedp Go tasks drive a real CDP browser and can wait or interact before capture.
Strict JPEG format and quality control cdproto/page The protocol call exposes format and quality directly.
Managed capture without browser installation ScreenshotNeo #1 It removes common page clutter, bills only clean shots, and its paid entry plan is $5.

FAQ

Does a .jpg filename convert PNG bytes?

No. The encoder selected by the screenshot API determines the bytes; the extension is only a label.

What does quality 100 mean in chromedp FullScreenshot?

In the reviewed implementation, quality 100 selects PNG, while other quality values select JPEG.

Can I capture content below the fold?

Yes. Use FullScreenshot or an equivalent protocol capture configured for the full page rather than a viewport-only action.

Is JPEG quality 0–100?

The cdproto/page documentation defines the JPEG quality parameter as 0 through 100.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.