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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

The reliable pattern is simple: encode each screenshot as JPEG in memory, prefix it with a fixed-width length, send the header and payload with sendall(), and have the receiver loop until it has collected exactly that many bytes. JPEG quality controls the size–fidelity trade-off; the fastest setting for your application must be measured with representative screens because encoding CPU time, network time and visual quality all interact.

What makes screenshot sockets slow or unreliable?

A screenshot is usually a large uncompressed pixel buffer. Sending that buffer directly consumes bandwidth, while treating a TCP connection as if it preserved message boundaries can merge or split frames unpredictably. TCP provides an ordered byte stream, not individual messages. You therefore need two separate solutions:

  • Compression: encode the image as JPEG before transmission.
  • Framing: send the payload length first, then receive until that declared length has arrived.

JPEG is lossy, so it is appropriate when small visual differences are acceptable. Text-heavy interfaces can reveal ringing or blurred colored edges sooner than photographs. Test the actual screens your users will view.

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

Install Pillow and choose an image source

Install Pillow in the environment that captures or encodes the image:

#1 Best Overall
Sale
TP-Link USB to Ethernet Adapter,Support Nintendo Switch,1Gbps,Plug and Play
  • 𝐇𝐢𝐠𝐡-𝐒𝐩𝐞𝐞𝐝 𝐔𝐒𝐁 𝐄𝐭𝐡𝐞𝐫𝐧𝐞𝐭 𝐀𝐝𝐚𝐩𝐭𝐞𝐫 - UE306 is a USB 3.0 Type-A to RJ45 Ethernet adapter that adds a reliable wired network port to your laptop, tablet, or Ultrabook. It delivers fast and stable 10/100/1000 Mbps wired connections to your computer or tablet via a router or network switch, making it ideal for file transfers, HD video streaming, online gaming, and video conferencing.
  • 𝐔𝐒𝐁 𝟑.𝟎 𝐟𝐨𝐫 𝐅𝐚𝐬𝐭𝐞𝐫, 𝐌𝐨𝐫𝐞 𝐒𝐭𝐚𝐛𝐥𝐞 𝐃𝐚𝐭𝐚 𝐓𝐫𝐚𝐧𝐬𝐟𝐞𝐫𝐬- Powered via USB 3.0, this adapter provides high-speed Gigabit Ethernet without the need for external power(10/100/1000Mbps). Backward compatible with USB 2.0/1.1, it ensures reliable performance across a wide range of devices.
  • 𝐒𝐮𝐩𝐩𝐨𝐫𝐭𝐬 𝐍𝐢𝐧𝐭𝐞𝐧𝐝𝐨 𝐒𝐰𝐢𝐭𝐜𝐡- Easily connect your Nintendo Switch to a wired network for faster downloads and a more stable online gaming experience compared to Wi-Fi.
  • 𝐏𝐥𝐮𝐠 𝐚𝐧𝐝 𝐏𝐥𝐚𝐲- No driver required for Nintendo Switch, Windows 11/10/8.1/8, and Linux. Simply connect and enjoy instant wired internet access without complicated setup.
  • 𝐁𝐫𝐨𝐚𝐝 𝐃𝐞𝐯𝐢𝐜𝐞 𝐂𝐨𝐦𝐩𝐚𝐭𝐢𝐛𝐢𝐥𝐢𝐭𝐲- Supports Nintendo Switch, PCs, laptops, Ultrabooks, tablets, and other USB-powered web devices; works with network equipment including modems, routers, and switches.
python -m pip install Pillow

Your capture code may return a Pillow Image, a PNG file, or another pixel format. The examples below accept a Pillow image. If the source contains transparency, decide what should replace it first; JPEG has no alpha channel.

Encode a screenshot as JPEG in memory

Use io.BytesIO to avoid a temporary file. Convert deliberately to RGB, because JPEG encoders expect a compatible color mode.

from io import BytesIO
from PIL import Image

def encode_jpeg(image: Image.Image, quality: int = 80, optimize: bool = True,
                subsampling: int | str = "keep") -> bytes:
    """Return JPEG bytes for a Pillow image."""
    if image.mode in ("RGBA", "LA") or "transparency" in image.info:
        # Choose the background your product requires.
        background = Image.new("RGB", image.size, "white")
        rgba = image.convert("RGBA")
        background.paste(rgba, mask=rgba.getchannel("A"))
        image = background
    else:
        image = image.convert("RGB")

    out = BytesIO()
    save_args = {
        "format": "JPEG",
        "quality": quality,
        "optimize": optimize,
    }
    if subsampling != "keep":
        save_args["subsampling"] = subsampling
    image.save(out, **save_args)
    return out.getvalue()

Pillow documents quality from 0 to 95 (default 75) and cautions that values above 95 produce much larger files for little visual benefit. Its tutorial uses RGB conversion with quality=80, optimize=True as an example, not as a universal optimum (JPEG format options; Pillow tutorial).

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.

What optimize=True changes

Optimization performs an additional encoder pass to select more efficient JPEG tables. It can reduce the payload, but it also consumes CPU. Keep it only when the saved transfer time outweighs that extra work on your machines.

Quality and subsampling

Compare several quality values, such as 60, 75, 80 and 90, using identical screenshots. Pillow defines subsampling 0, 1 and 2 as 4:4:4, 4:2:2 and 4:2:0 respectively (Pillow JPEG presets). Lower chroma resolution often saves bytes but can damage colored text and sharp UI edges. Inspect readability, not just file size.

Rank #2
Amazon Basics USB 3.0 to 10/100/1000 Gigabit Ethernet Internet Adapter, Compatible with Windows and macOS, Black
  • Connects a USB 3.0 device (computer/laptop) to a router, modem, or network switch to deliver Gigabit Ethernet to your network connection. Does not support Smart TV or gaming consoles (e.g.Nintendo Switch).
  • Supported features include Wake-on-LAN function, Green Ethernet & IEEE 802.3az-2010 (Energy Efficient Ethernet)
  • Supports IPv4/IPv6 pack Checksum Offload Engine (COE) to reduce Cental Processing Unit (CPU) loading
  • Compatible with Windows 8.1 or higher, Mac OS

Use a length-prefixed TCP protocol

A minimal frame is 4-byte unsigned length | JPEG payload. Encode the length in network byte order with struct.pack("!I", length). The receiver must reject impossible lengths before allocating memory and treat a closed connection before the full payload as an incomplete frame.

Complete sender

import socket
import struct
import time
from PIL import ImageGrab

HOST = "127.0.0.1"
PORT = 5000
MAX_FRAME = 25 * 1024 * 1024

def encode_jpeg(image, quality=80, optimize=True):
    from io import BytesIO
    image = image.convert("RGB")
    out = BytesIO()
    image.save(out, format="JPEG", quality=quality, optimize=optimize)
    return out.getvalue()

def send_frame(sock, payload: bytes) -> None:
    if not 0 < len(payload) <= MAX_FRAME:
        raise ValueError("JPEG payload exceeds configured limit")
    sock.sendall(struct.pack("!I", len(payload)))
    sock.sendall(payload)

with socket.create_connection((HOST, PORT), timeout=10) as sock:
    while True:
        started = time.perf_counter()
        screenshot = ImageGrab.grab()
        jpeg = encode_jpeg(screenshot, quality=80, optimize=True)
        encode_ms = (time.perf_counter() - started) * 1000
        send_frame(sock, jpeg)
        print(f"encoded={encode_ms:.1f} ms bytes={len(jpeg)}")
        time.sleep(0.1)

sendall() keeps sending until the complete buffer is sent or an error occurs. A single send() call may write only part of the buffer, so do not use it without your own loop. These semantics are documented by the Python 3.13 socket documentation.

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

Complete receiver

import socket
import struct
from io import BytesIO
from PIL import Image

HOST = "0.0.0.0"
PORT = 5000
MAX_FRAME = 25 * 1024 * 1024

def recv_exact(sock: socket.socket, count: int) -> bytes:
    chunks = []
    remaining = count
    while remaining:
        chunk = sock.recv(min(64 * 1024, remaining))
        if not chunk:
            raise ConnectionError("peer closed before the frame was complete")
        chunks.append(chunk)
        remaining -= len(chunk)
    return b"".join(chunks)

def recv_frame(sock: socket.socket) -> bytes:
    header = recv_exact(sock, 4)
    (length,) = struct.unpack("!I", header)
    if length == 0 or length > MAX_FRAME:
        raise ValueError(f"invalid frame length: {length}")
    return recv_exact(sock, length)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
    listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listener.bind((HOST, PORT))
    listener.listen(1)
    conn, address = listener.accept()
    with conn:
        print("connected:", address)
        while True:
            jpeg = recv_frame(conn)
            image = Image.open(BytesIO(jpeg))
            image.load()                 # force decoding while bytes are present
            image.save("latest.jpg")     # replace with display or application logic
            print(image.size, len(jpeg))

recv() can return fewer bytes than requested, including part of the four-byte header. The recv_exact loop handles both the header and payload. recv_into() is another option when you want to fill a preallocated buffer; it also receives up to the requested byte count.

Measure the setting instead of guessing

There is no universal quality, subsampling or frame-rate setting for this workload. Build a small test using the same capture source, machine and network conditions you will deploy.

Metric How to record it Why it matters
JPEG bytes len(jpeg) Determines bandwidth and buffering pressure
Encode time Timer around image.save Shows CPU cost, including optimization
Socket transfer time Timer around the two sendall calls Shows network and receiver back-pressure
Decode time Timer around Image.open and load Captures receiver CPU cost
End-to-end latency Timestamp capture through display/processing Matches what the user experiences
Sustained frame rate Count completed frames over a fixed interval Reveals queue growth and dropped work

Test text-heavy pages, flat-color dashboards and photographic content separately. Keep the screenshot set and network path identical while comparing quality and subsampling. A smaller file is not faster if encoding or decoding dominates.

Rank #3
USB A/C to Ethernet Adapter, 3xUSB3.0 and 1000M RJ45 Network hub for Laptop
  • [Expansion Ports] The USB C to Ethernet Adapter expands the device to three USB 3.0 ports and one Gigabit Ethernet port. Provides you more peripheral ports while maintaining a stable network connection, plug and play, no driver required.
  • [Gigabit Network Port] ALL-LUCKY USB Ethernet Adapter transmission rate up to 1000Mbps, also compatible with 10/100Mbps bandwidth. It allows you to enjoy a smooth and stable network connection and avoid too much lag. (Note: To reach 1Gbps, please use CAT6 or above Ethernet cable connection)
  • [Convertible Connector]This usb hub with ethernet not only has USB-A connector, but also can be converted to USB-C connector, so that you can easily convert the connector according to the device port, improve the convenience of use.
  • [High-Speed Data Transfer] The usb to ethernet adapter adopts USB 3.0 transmission technology, supports up to 5Gbps transmission rate, and is compatible with USB 2.0(480Gbps),USB 1.0(12Mbps), easily transfer video, files and other data for you in seconds. (Note: Maximum output current is 900mA, does not support charging devices.)
  • [Widely Compatible]The usb c ethernet adapter for iMac, MacBook Pro, iPad Pro, XPS and many other devices. Compatible with Windows 11/10/8.1/8, Mac OS, iPad OS, Chrome OS.(Note: Driver is required on Win 7) It can be used in office, school, library and other occasions, compact and portable, easy to carry around.

Reliability, flow control and security

Prevent unbounded memory use

Set a maximum frame size on both ends and reject a declared length above it before collecting bytes. You can also impose socket timeouts so a peer cannot hold a connection indefinitely while sending a partial frame.

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

Handle disconnects and back-pressure

Catch TimeoutError, ConnectionError and OSError. Reconnect with a bounded backoff and decide whether to drop stale screenshots rather than queueing unlimited frames. For live previews, keeping the newest frame is often preferable to transmitting every historical frame.

Protect the connection

The example is plain TCP. On an untrusted network, add authentication and transport encryption (for example, TLS) and validate who may connect. Never trust a length prefix, image metadata or a requested output path.

Choose a protocol version

If the format may evolve, reserve a version or flags byte in a larger header. Document byte order, maximum length, JPEG parameters and what a clean shutdown means. A length prefix solves framing; it does not provide encryption, integrity authentication or message semantics.

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

Troubleshooting common failures

The receiver shows corrupted or merged images

Cause: it assumes one recv() equals one frame, or the sender omitted framing. Fix: send the four-byte length before every payload and use recv_exact for both header and body.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Anker USB C to Ethernet Adapter, Portable 1 Gbps Network Hub
  • The Anker Advantage: Join the 65 million+ powered by our leading technology.
  • Instant Internet: Connect to the internet instantly from virtually any USB-C 3.0 device, and enjoy stable connection speeds of up to 1 Gbps.
  • Lightweight and Compact: The space-saving and portable design measures just over half an inch thick and weighs about the same as a AA battery.
  • Premium Build: Features a sleek aluminum exterior and braided-nylon cable to complement the design of high-end devices.
  • What You Get: PowerExpand USB-C to Gigabit Ethernet Adapter, welcome guide, 18-month worry-free warranty, and friendly customer service.

Only part of a screenshot arrives

Cause: a partial send() or a disconnect. Fix: use sendall(), retain the length prefix, and treat EOF before the declared length as an incomplete frame.

cannot write mode RGBA as JPEG

Cause: JPEG does not store alpha. Composite the image over an intentional background, then convert to RGB before saving.

CPU usage rises after enabling optimization

optimize=True adds an encoder pass. Compare end-to-end latency and sustained frame rate with it disabled; retain it only when the smaller payload compensates for CPU time.

Frames become unreadable at a lower quality

Raise quality or use a less aggressive subsampling value, then measure the resulting bytes and latency. Colored labels and one-pixel UI lines are useful test cases.

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

The connection hangs

Check that the client and server agree on host, port, byte order and maximum length. Add timeouts, log the declared and received byte counts, and verify that both sides close or reuse the connection consistently.

Best Value
Sale
BENFEI USB 3.0 to Ethernet Adapter, USB C to RJ45 Gigabit LAN (1000Mbps) Network Adapter, Compatible with MacBook/Pro/Air, Surface Pro, Windows 11/10/8/7, Mac OS [Aluminium Shell&Nylon Cable]
  • COMPACT DESIGN - The compact-designed portable BENFEI USB A/C to Ethernet adapter connects your computer or tablet to a router,modem or network switch for network connection. It adds a standard RJ45 port to your Ultrabook, notebook or Macbook Air for file transferring, video conferencing, gaming, and HD video streaming.
  • SUPERIOR STABILITY - Built-in advanced IC chip works as the bridge between RJ45 Ethernet cable and your USB A/C devices. The driver-free installation with native driver support in Chrome, Mac, and Windows OS; The USB A/C Ethernet adapter dongle supports important performance features including Wake-on-Lan (WoL), Full-Duplex (FDX) and Half-Duplex (HDX) Ethernet, Crossover Detection, Backpressure Routing, Auto-Correction (Auto MDIX).
  • INCREDIBLE PERFORMANCE - Supports full 10/100/1000Mbps gigabit ethernet performance over USB A/C's 5Gbps bus, faster and more reliable than most wireless connections. Link and Activity LEDs. USB powered, no external power required. Backward compatible with USB 2.0/1.1.✅ To reach 1Gbps, make sure to use CAT6 & up Ethernet cables.
  • BROAD COMPATIBILITY - The USB A/C-Ethernet adapter is compatible with Windows 11/10/8.1/8/7/Vista/XP, Mac OSX 10.6/10.7/10.8/10.9/10.10/10.11/10.12, Linux kernel 3.x/2.6, Android and Chrome OS.Compatible with IEEE 802.3, IEEE 802.3u and IEEE 802.3ab. Supports IEEE 802.3az (Energy Efficient Ethernet).❌Do Not Support Windows RT. (NOT compatible with Nintendo Switch.)
  • 18 MONTH WARRANTY - Exclusive BENFEI Unconditional 18-month Warranty ensures long-time satisfaction of your purchase; Friendly and easy-to-reach customer service to solve your problems timely.

Or skip the browser setup

If your real task is obtaining clean website screenshots rather than streaming a local desktop capture, ScreenshotNeo provides a single HTTP request. It accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; those steps can be disabled individually. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, with the result identified by X-Page-Verdict and X-Billed headers.

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)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

See the ScreenshotNeo API documentation for the 63 capture options, including full-page lazy-image loading, CSS-selector elements, device presets, retina scale, PDF output, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, geolocation, caching, signed links, asynchronous webhooks, bulk capture and usage reporting. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients. Every plan includes every feature: 1,000 shots per month are free with no card; paid plans start at $5 for 3,000 shots.

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

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

Frequently Asked Questions

Can I use PNG instead of JPEG?

Yes, when lossless text or transparency is essential, but PNG is often larger for photographic or complex screenshots. Keep the same length-prefix protocol and measure the complete pipeline.

Why not send each frame as a separate TCP packet?

TCP packet boundaries are not exposed as application message boundaries. A length prefix (or another explicit framing protocol) is required regardless of how the sender writes.

Should I open a new socket for every screenshot?

Usually no. Reusing one authenticated connection avoids connection setup overhead, but reconnect when errors or idle timeouts occur and cap queued work.

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.

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.