Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Use Splash’s element PNG helper inside a Lua script: navigate to the page, wait until the target is rendered, select it with a CSS selector, and return element:png(). The smallest working script is:
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
local element = splash:select(args.css)
assert(element, "No element matched the CSS selector")
return element:png()
end
Send that script to Splash’s execute endpoint as lua_source, together with url and a selector argument. The half-second wait is only an example; replace it with a readiness condition that matches the page you are capturing.
How Splash’s single-element capture works
Splash renders JavaScript pages and exposes a Lua scripting API. In the script, splash:select(css) returns a handle for the first DOM element matching the CSS selector. Calling :png() on that handle produces the element’s rendered PNG as a binary image result. Returning the PNG directly avoids taking a full-page screenshot and cropping it later.
The selector is evaluated after navigation and any wait you specify. If no element matches, the example raises an explicit error instead of returning an apparently successful but empty image. Use a stable ID, data attribute, or narrowly scoped class whenever possible; selectors based on generated class names are more likely to break when the site changes.
#1 Best Overall
- CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
- WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
- A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents
Minimal Lua script
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
local element = splash:select(args.css)
assert(element, "No element matched the CSS selector")
return element:png()
end
What each line does
splash:go(args.url)loads the URL supplied by the caller and stops with an error if navigation fails.splash:wait(0.5)gives scripts and layout a chance to run. It is not a universal readiness guarantee.splash:select(args.css)finds the first matching element.assert(element, ...)turns a selector miss into a clear failure.element:png()returns the selected node as a PNG image.
Choosing a reliable wait
Use a fixed delay only when the page is predictable. For an application that inserts the node asynchronously, wait for a selector or for the page’s own state to become ready before selecting it. A delay that is too short captures an empty shell; a delay that is unnecessarily long reduces throughput. Splash’s timing and JavaScript behavior can vary with the deployed version and the target page, so verify the choice against the pages you actually crawl.
Calling the execute endpoint over HTTP
For an HTTP request, put the Lua source in the lua_source argument and provide the target URL and selector as request arguments. The response is the binary PNG returned by the script. The following cURL command uses a shell variable so the multiline script remains readable:
read -r -d '' LUA <<'EOF'
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
local element = splash:select(args.css)
assert(element, "No element matched the CSS selector")
return element:png()
end
EOF
curl -G "http://localhost:8050/execute"
--data-urlencode "lua_source=$LUA"
--data-urlencode "url=https://example.com"
--data-urlencode "css=main h1"
-o element.png
Change the Splash host, target URL, and selector for your deployment. If your shell does not support the shown here-document form, place the Lua in a file and URL-encode it with the HTTP client or SDK you use.
Python integrations
Python requests
This standalone example submits the same script to Splash and writes the binary response:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #2
- CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
- SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
import requests
lua = r'''
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
local element = splash:select(args.css)
assert(element, "No element matched the CSS selector")
return element:png()
end
'''
params = {
"lua_source": lua,
"url": "https://example.com",
"css": "main h1",
}
response = requests.get("http://localhost:8050/execute", params=params, timeout=90)
response.raise_for_status()
with open("element.png", "wb") as image:
image.write(response.content)
Use a timeout that allows the slowest pages in your crawl to finish, but enforce an upper bound so one stalled render does not occupy a worker indefinitely. Log the URL and selector with HTTP status and response headers when diagnosing failures.
Scrapy with scrapy-splash
The Scrapy integration submits Lua to the execute endpoint through SplashRequest:
import scrapy
from scrapy_splash import SplashRequest
SCRIPT = r'''
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
local element = splash:select(args.css)
assert(element, "No element matched the CSS selector")
return element:png()
end
'''
class ElementSpider(scrapy.Spider):
name = "element"
def start_requests(self):
yield SplashRequest(
url="https://example.com",
endpoint="execute",
args={"lua_source": SCRIPT, "css": "main h1"},
callback=self.parse_image,
)
def parse_image(self, response):
with open("element.png", "wb") as image:
image.write(response.body)
Depending on your scrapy-splash configuration and response mode, the script may return a binary body or a JSON object containing encoded data. Follow the response type configured by your integration: decode base64 only when the response is actually encoded JSON, and do not base64-decode an already binary PNG.
When to use a padded region instead
element:png() is the simplest option, but it gives you the element’s own box. Use a region screenshot when you need explicit padding, a crop assembled from DOM geometry, or a bounding box that you calculate yourself.
Rank #3
- Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
- Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
- Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
- In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
- Ultra-thin bezels: Maximize your viewing experience with thin bezels.
function pad(r, amount)
return {r[1] - amount, r[2] - amount,
r[3] + amount, r[4] + amount}
end
function main(splash, args)
local get_bbox = splash:jsfunc([[
function(css) {
var el = document.querySelector(css);
if (!el) return null;
var r = el.getBoundingClientRect();
return [r.left, r.top, r.right, r.bottom];
}
]])
assert(splash:go(args.url))
assert(splash:wait(0.5))
splash:set_viewport_full()
local bbox = get_bbox(args.css)
assert(bbox, "No element matched the CSS selector")
return splash:png{region=pad(bbox, args.pad or 0)}
end
Region rules that matter
- The region order is
{left, top, right, bottom}, not x, y, width, height. getBoundingClientRect()returns coordinates relative to the current scroll position.- Call
splash:set_viewport_full()before rendering when the target could otherwise be clipped by the viewport. - The documented region method cannot capture content outside the current viewport unless the viewport is adjusted appropriately; test long or dynamically scrolling pages rather than assuming a full-page crop.
- Padding can produce negative left or top values. Clamp or validate them if your target pages place the element at the viewport edge.
Viewport, full-page, and scaling choices
Element capture does not require render_all=1. That option extends the viewport to the whole page, is intended for full-page rendering, and requires a non-zero wait. Enabling it by default adds work without improving a target that already fits in the viewport. For a region workflow, splash:set_viewport_full() is the documented way to reduce viewport clipping.
Splash’s PNG rendering also supports scale_method values raster and vector. Vector scaling can be faster and sharper in some cases, but the documentation warns that it can cause rendering issues. Treat it as a per-site rendering choice: compare output on representative pages before making it your default.
Selectors, dynamic content, and difficult pages
Selector misses
Check the selector in the page’s DOM, not just the visual source. A class may be added only after JavaScript runs, and a selector that matches several nodes returns the first match. Narrow it with an ancestor, ID, or data attribute when the page contains repeated cards.
Late layout changes
Images, fonts, ads, and client-side data can change the box after the first paint. Wait for a page-specific marker or for the target selector to exist, then allow any known layout transition to settle. If the element is present but the image is blank, the problem is readiness rather than selection.
Rank #4
- CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
- SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
- MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
- KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
- INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient
Shadow DOM and iframes
A normal document selector does not automatically cross every encapsulation boundary. The reviewed Splash workflow does not establish guarantees for cross-origin iframe targets. If the element lives in an iframe or shadow tree, verify what the deployed Splash version can access and consider capturing the frame’s own URL when it is available.
Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
| “No element matched” assertion | Wrong selector, page not ready, or element inside another document | Inspect the rendered DOM, wait for the insertion point, narrow or correct the selector, and check iframe or shadow boundaries. |
| PNG is blank or shows a skeleton | Asynchronous data or images have not finished | Replace the illustrative delay with a page-specific readiness wait and verify that required resources are reachable. |
| Only part of the target appears | Viewport clipping or a region outside the visible area | Use splash:set_viewport_full() for region captures and check scroll-relative coordinates. |
| HTTP request returns an error page | Lua runtime error, navigation failure, or malformed arguments | Test the script in Splash directly, keep assert messages, URL-encode lua_source, and log the returned status and body. |
| Scrapy callback cannot decode output | Response mode mismatch | Inspect whether the endpoint returned binary PNG or JSON/base64, then handle exactly that format; do not decode binary data as base64. |
| Output differs between runs | Animation, changing content, fonts, or network timing | Disable or wait through animations where possible, use deterministic test URLs, and capture after the same readiness condition. |
Operational and cost considerations
Element screenshots are smaller and usually cheaper to store and transfer than full-page images, but Splash still has to load and render the page. Reuse a Splash service rather than starting a browser for every request, cap concurrency to the resources available, and set request timeouts. Cache captures when the source and selector are unchanged; invalidate that cache when content or CSS changes. Keep the selector and wait policy alongside the capture code so a visual regression can be reproduced.
There is no universal wait duration, rendering speed, or cross-site success rate established for this workflow. Measure on your own Splash version, network, viewport, and page mix. Treat bot checks, login walls, consent dialogs, and network failures as page-specific conditions that may require headers, cookies, or a different capture path.
Or skip the browser setup
For a hosted one-call option, ScreenshotNeo accepts a URL and returns a PNG, JPEG, WebP, or PDF. It removes cookie and consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Recommended Free Tools
Here is the equivalent cURL request (see the ScreenshotNeo documentation for all options):
Best Value
- 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
- 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
- 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo includes full-page and CSS-selector captures, custom JavaScript and CSS, waits, request blocking, cookies and headers, device presets, viewport and retina controls, resizing, caching with a chosen TTL, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and PDF settings. Every plan includes every feature. The Free plan provides 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Frequently Asked Questions
Does Splash capture the first matching element or every match?
splash:select(css) selects the first matching element. Use a more specific selector or write a script that iterates over matches when you need multiple images.
Can I return JPEG or WebP from element:png()?
The documented element helper returns PNG data. Converting the result is a separate image-processing step; do not assume the Lua helper changes formats.
Should I use render.png instead of execute?
render.png is suited to whole-page captures. A custom Lua script sent to execute is the direct workflow for selecting one element or calculating a padded region.
Where are Splash region coordinates measured from?
They are ordered left, top, right, bottom and are relative to the current scroll position.
Quick Recap
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.

