Notes
Atom FeedWebsite Image Tags
Image tags and sizes used for link previews, favicons, and app icons. Platforms change these limits without much notice, and some are not officially documented; these were last checked in November 2026. Values marked with * are from memory or third-party sources, and other values may also rely on secondary sources.
Recommended
This set covers link previews, browser tabs, search results, and home screens on current platforms:
| File | Size | Used for |
|---|---|---|
preview.png |
1200x630 | Large link previews on social and chat apps |
favicon.ico |
32x32 | Older browsers and automated tools |
icon.svg |
Scalable | Browser tabs |
apple-touch-icon.png |
180x180 | iOS home screen |
icon-192.png |
192x192 | Android home screen |
icon-512.png |
512x512 | Progressive web app install |
icon-maskable-512.png |
512x512 | Android adaptive icons |
<meta property="og:image" content="https://example.com/preview.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Description of the image">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/manifest.webmanifest">
The icons section of manifest.webmanifest is below; web app install also needs name or short_name, start_url, and display:
{
"icons": [
{"src": "/icon-192.png", "type": "image/png", "sizes": "192x192"},
{"src": "/icon-512.png", "type": "image/png", "sizes": "512x512"},
{"src": "/icon-maskable-512.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable"}
]
}
The 1200x630 image suits pages with a designed preview image, and fills the width of a message in chat apps. If the only image is a logo or icon, use a 280x280 square image instead so it appears as a small thumbnail next to the title, set the og:image:width and og:image:height tags to match, and use twitter:card summary. Do not reuse the 512x512 app icon for previews, since WhatsApp will show it at full size.
- Use absolute
https://URLs for preview images - Use PNG or JPEG for preview images, and keep important content centered in case of cropping
- Keep preview images under 600 KB, which is WhatsApp’s documented limit
- Serve
favicon.icofrom the site root, since some tools only look there - Give the Apple touch icon an opaque background and square corners
- Keep the logo in a maskable icon inside a centered circle with a radius of 40% of the icon width
sizes="32x32"reportedly stops Chrome from preferring the ICO over the SVG- Change the filename when replacing an image, since platforms and browsers cache them aggressively
Exhaustive
Link Previews
| Tag | Used by | Size | Aspect ratio | File limit |
|---|---|---|---|---|
og:image |
1200x630 (600x315 for large, 200x200 minimum) | 1.91:1 | 8 MB | |
og:image |
1200x627 minimum | 1.91:1 | 5 MB | |
og:image |
300 pixels wide or more for large | 4:1 or narrower | 600 KB | |
og:image |
Discord | 1200x630 in Discord’s example; no size requirement | Undocumented | Undocumented |
og:image |
Signal | Any size meeting the rules below; 1200x630 and 280x280 work | See the rules below | Undocumented |
og:image |
Slack, iMessage, others | 1200x630 for large*, 280x280 for thumbnail* | 1.91:1 for large*, 1:1 for thumbnail* | Undocumented |
twitter:image (summary_large_image) |
X/Twitter | 1200x600* (300x157 minimum*, 4096x4096 maximum*) | 2:1* | 5 MB* |
twitter:image (summary) |
X/Twitter | 400x400* (144x144 minimum*) | 1:1* | 5 MB* |
X falls back to og:image when twitter:image is missing, but needs twitter:card to choose the card format.* X accepts JPG, PNG, WEBP, and GIF, and only uses the first frame of an animated GIF.*
Chat apps choose between a large preview and a small thumbnail using either the image’s dimensions or the page’s tags:
| App | Large preview when | Source |
|---|---|---|
| Discord | twitter:card is summary_large_image |
Documentation |
| At least 300 pixels wide, 4:1 or narrower, and under 600 KB | Documentation | |
| Slack | About 400 pixels wide or more | One contributor’s report, not documented |
| Signal Desktop | Width and height are at least 200 pixels, and not within 5% of square | Source code |
| Signal Android | Width is at least 150dp in device pixels, and wider than tall | Source code |
| Signal iOS | Width is at least about 2x the maximum message width, and height is at least 33% of that | Source code |
WhatsApp documents the requirements for a large preview, and says it may relax them or revert to small previews without giving a threshold, so a thumbnail under 300 pixels wide comes from the same report. Signal does not use a single width threshold, so a large square image may still appear as a thumbnail. Discord shows a small image beside the text by default, accepts PNG, GIF, JPEG, WEBP, and AVIF, and fetches the image to measure it unless og:image:width and og:image:height are set.
Preview Image Properties
| Tag | Meaning |
|---|---|
og:image:url |
Same as og:image |
og:image:secure_url |
HTTPS alternate URL |
og:image:type |
MIME type |
og:image:width |
Width in pixels |
og:image:height |
Height in pixels |
og:image:alt |
Description of the image |
twitter:image:alt |
Description of the image, 420 characters maximum* |
Structured Data
| Tag | Used by | Size | Aspect ratio |
|---|---|---|---|
JSON-LD image on an Article |
Google Search | Width x height of at least 50,000 pixels | Provide 16:9, 4:3, and 1:1 |
<meta itemprop="image"> |
Schema.org microdata consumers | Same as og:image* |
1.91:1* |
Favicons
| Tag | Size | Used by |
|---|---|---|
<link rel="icon" type="image/svg+xml"> |
Scalable | Modern browsers, including Safari 26 and later |
<link rel="icon" type="image/png"> |
16x16*, 32x32* | Browser tabs |
<link rel="icon"> or /favicon.ico |
16x16*, 32x32*, 48x48* | Older browsers and automated tools |
<link rel="icon"> |
Square, above 48x48 (8x8 minimum) | Google Search results |
<link rel="shortcut icon"> |
Same as icon |
Older alternative to icon |
App Icons
| Tag | Size | Used by |
|---|---|---|
<link rel="apple-touch-icon"> |
180x180 | Current iPhones* |
<link rel="apple-touch-icon"> |
167x167 | iPad Pro* |
<link rel="apple-touch-icon"> |
152x152 | iPad* |
<link rel="apple-touch-icon"> |
57x57*, 60x60*, 72x72*, 76x76*, 114x114*, 120x120*, 144x144* | Older iOS devices |
/apple-touch-icon.png |
180x180 | Safari, without a <link> tag |
Web manifest icons |
192x192, 512x512 | Android, web app install |
Web manifest icons with maskable |
512x512 | Android adaptive icons |
Legacy
These are safe to skip on new sites:
| Tag | Size | Used by |
|---|---|---|
<link rel="image_src"> |
Same as og:image* |
Older scrapers* |
twitter:image:src |
Same as twitter:image* |
Older name for twitter:image* |
<link rel="apple-touch-icon-precomposed"> |
Same as apple-touch-icon* |
iOS 6 and earlier |
<link rel="mask-icon"> |
Scalable, single color SVG | Safari pinned tabs before Safari 12* |
<meta name="msapplication-TileImage"> |
144x144* | Windows 8 tiles* |
browserconfig.xml |
70x70*, 150x150*, 310x150*, 310x310* | Windows 8.1 and 10 tiles* |
Sources:
- The Open Graph Protocol
- Meta; Images in Link Shares
- LinkedIn; Make Your Website Shareable
- X; Summary Card with Large Image
- Discord; Link Previews
- Google; Favicon in Search Results
- Google; Article Structured Data
- Evil Martians; How to Favicon
- Can I Use; SVG Favicons
- Apple; Configuring Web Applications
- web.dev; Maskable Icons
Timezone Boundaries Mapped
The IANA timezone database contains over 400 timezone entries mostly named
as “<continent>/<city>”. They are meticulously cataloged to group regions of
the world not only by UTC offset but also by the presence and timing of
Daylight Savings as well as current and historical timezone policy changes.
The github.com/albertyw/localtimezone repository maps latitude/longitude data to IANA timezone names. It is based on github.com/evansiroky/timezone-boundary-builder which in term is based on OpenStreetMap. From that data, I generated an Interactive Map:

Comparing WebdriverIO Firefox vs Chrome
Chrome recently started releasing arm64 linux builds which meant that my ubuntu-on-m1-mac can run webdriverio (wdio) builds on both firefox and chrome (it was previously stuck doing only firefox builds).
I therefore compared build speed of codemancer on firefox versus chrome.
wdio.conf.ts Configuration
Firefox:
capabilities: [{
maxInstances: 5,
browserName: 'firefox',
'moz:firefoxOptions': {
args: ['-headless'],
},
acceptInsecureCerts: true,
}],
Chrome:
capabilities: [{
maxInstances: 5,
browserName: 'chrome',
'goog:chromeOptions': {
args: ['--headless=new', '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu'],
},
acceptInsecureCerts: true,
}],
For Arm architectures, the most recent version of wdio still depends on
@puppeteer/browsers at ^2.2.0 which doesn’t correctly fetch Chrome’s Arm builds.
Therefore, these additional configs need to be added to wdio.conf.ts:
// Set these environment variables to manually-downloaded chrome executables
// For the satantime/puppeteer-node:26-trixie-slim docker images, use
// CHROME_BINARY=/usr/bin/chromium
// CHROMEDRIVER_BINARY=/usr/bin/chromedriver
const chromeBinary = process.env.CHROME_BINARY;
const chromedriverBinary = process.env.CHROMEDRIVER_BINARY;
// inside capabilities
'goog:chromeOptions': {
args: [/* ... */],
...(chromeBinary ? { binary: chromeBinary } : {}),
},
...(chromedriverBinary ? { 'wdio:chromedriverOptions': { binary: chromedriverBinary } } : {}),
Benchmarks
I benchmarked both browsers on codemancer unit tests over 30 iterations.
On Arm64, Chromium 151 against Firefox 155:
browser runs min median mean max
chrome 10 4934ms 6593ms 7143ms 10054ms
firefox 10 7055ms 11426ms 17360ms 50600ms
On x86_64, Chromium 152 against Firefox ESR 140:
browser runs min median mean max
chrome 10 4433ms 5265ms 6798ms 15222ms
firefox 10 6645ms 9119ms 18613ms 56509ms
References
- WebdriverIO browser runner
- Chrome for Testing availability
- Bringing Chrome to ARM64 Linux Devices
- puppeteer#14258: Installing Chrome with
--platform linux_arminstalls x86-64 binary
Geolocation Precision
Latitude/longitude and other geolocation systems can express a position at exponentially increasing precision as you add digits or levels. Below are tables of the approximate ground precision of several coordinate systems.
One degree of latitude is roughly constant everywhere (~111 km), but one degree of longitude shrinks toward the poles as the meridians converge, scaling with the cosine of the latitude. For the latitude/longitude systems below, the east/west distance is therefore given at several latitudes. Grid systems like H3, S2, Geohash, and Plus Codes are designed to be nearly uniform across the globe, so their precision is instead given as an approximate cell edge length and area.
Degrees with decimals (Degrees)
| Degrees | Decimals | E/W at equator | E/W at 20° N/S | E/W at 45° N/S | E/W at 70° N/S |
|---|---|---|---|---|---|
| 1 | 0 | 111.3 km | 104.6 km | 78.72 km | 38.07 km |
| 0.1 | 1 | 11.13 km | 10.46 km | 7.872 km | 3.807 km |
| 0.01 | 2 | 1.113 km | 1.046 km | 787.2 m | 380.7 m |
| 0.001 | 3 | 111.3 m | 104.6 m | 78.72 m | 38.07 m |
| 0.0001 | 4 | 11.13 m | 10.46 m | 7.872 m | 3.807 m |
| 0.00001 | 5 | 1.113 m | 1.046 m | 78.7 cm | 38.1 cm |
Degrees-minutes-seconds (DMS)
| Precision | Unit | E/W at equator | E/W at 20° N/S | E/W at 45° N/S | E/W at 70° N/S |
|---|---|---|---|---|---|
| 1°00′00″ | degree | 111.3 km | 104.6 km | 78.72 km | 38.07 km |
| 0°01′00″ | arcminute | 1.855 km | 1.743 km | 1.312 km | 634.6 m |
| 0°00′01″ | arcsecond | 30.92 m | 29.06 m | 21.87 m | 10.58 m |
| 0°00′00.1″ | 0.1 arcsecond | 3.092 m | 2.906 m | 2.187 m | 1.058 m |
| 0°00′00.01″ | 0.01 arcsecond | 30.9 cm | 29.1 cm | 21.9 cm | 10.6 cm |
Geohash (base32)
Each geohash character adds 5 bits, alternating between longitude and latitude, so cells are square at odd lengths and 2:1 (wider than tall) at even lengths.
| Length | Cell size (lng × lat) | Ground size at equator |
|---|---|---|
| 1 | 45° × 45° | 5,009 km × 5,009 km |
| 2 | 11.25° × 5.625° | 1,252 km × 626.2 km |
| 3 | 1.406° × 1.406° | 156.5 km × 156.5 km |
| 4 | 0.3516° × 0.1758° | 39.14 km × 19.57 km |
| 5 | 0.04395° × 0.04395° | 4.892 km × 4.892 km |
| 6 | 0.01099° × 0.005493° | 1.223 km × 611.5 m |
| 7 | 0.001373° × 0.001373° | 152.9 m × 152.9 m |
| 8 | 0.0003433° × 0.0001717° | 38.22 m × 19.11 m |
| 9 | 0.00004292° × 0.00004292° | 4.777 m × 4.777 m |
Plus Codes (Open Location Code)
| Code length | Cell size | Ground size at equator |
|---|---|---|
| 2 | 20° | 2,226 km |
| 4 | 1° | 111.3 km |
| 6 | 0.05° | 5.566 km |
| 8 | 0.0025° | 278.3 m |
| 10 | 0.000125° | 13.92 m |
| 11 | 0.000025° | 2.783 m |
| 12 | 0.000005° | 55.7 cm |
H3 hexagonal geospatial indexing
| Resolution | Avg edge length | Avg cell area |
|---|---|---|
| 0 | 1,281 km | 4,357,449 km² |
| 1 | 483 km | 609,788 km² |
| 2 | 183 km | 86,802 km² |
| 3 | 68.98 km | 12,393 km² |
| 4 | 26.07 km | 1,770 km² |
| 5 | 9.854 km | 253 km² |
| 6 | 3.725 km | 36.13 km² |
| 7 | 1.406 km | 5.161 km² |
| 8 | 531.4 m | 0.7373 km² |
| 9 | 200.8 m | 0.1053 km² |
| 10 | 75.86 m | 0.01505 km² |
| 11 | 28.66 m | 2,150 m² |
| 12 | 10.83 m | 307 m² |
| 13 | 4.092 m | 43.9 m² |
| 14 | 1.546 m | 6.27 m² |
| 15 | 58.4 cm | 0.895 m² |
S2
| Level | Avg edge length | Avg cell area |
|---|---|---|
| 0 | 7,842 km | 85,011,012 km² |
| 4 | 490 km | 332,074 km² |
| 8 | 30.63 km | 1,297 km² |
| 10 | 7.658 km | 81.07 km² |
| 12 | 1.915 km | 5.067 km² |
| 16 | 119.7 m | 0.0198 km² |
| 20 | 7.479 m | 77.3 m² |
| 24 | 46.7 cm | 0.302 m² |
| 28 | 2.92 cm | 11.8 cm² |
| 30 | 7.3 mm | 0.737 cm² |
UTM (Universal Transverse Mercator)
UTM projects each of 60 longitudinal zones (each 6° wide) onto a flat grid and expresses a position as an easting/northing in meters within the zone. Because the coordinate is already metric, the precision is simply how many digits of the easting/northing you keep.
| Easting/northing digits | Rounded to | Ground precision |
|---|---|---|
| 550__ | 10 km | 10 km |
| 5502_ | 1 km | 1 km |
| 55020 | 100 m | 100 m |
| 550200 | 10 m | 10 m |
| 5502001 | 1 m | 1 m |
| 5502001.0 | 0.1 m | 10 cm |
MGRS (Military Grid Reference System)
MGRS is a grid-based alphanumeric encoding built on UTM (and UPS at the poles): a grid-zone designator, a 100 km square identifier, then equal numbers of easting and northing digits. Each added digit pair narrows the cell tenfold.
| Digits (per axis) | Example | Ground precision |
|---|---|---|
| 0 | 10SEG | 100 km |
| 1 | 10SEG12 | 10 km |
| 2 | 10SEG1234 | 1 km |
| 3 | 10SEG123456 | 100 m |
| 4 | 10SEG12345678 | 10 m |
| 5 | 10SEG1234567890 | 1 m |
Maidenhead locator system
Used by amateur-radio operators, Maidenhead alternates letter and number pairs (field → square → subsquare → …) to encode longitude and latitude. Cells are 2:1, wider than tall, because longitude spans 360° against latitude’s 180°.
| Characters | Cell size (lng × lat) | Ground size at equator |
|---|---|---|
| 2 (field) | 20° × 10° | 2,226 km × 1,113 km |
| 4 (square) | 2° × 1° | 223 km × 111 km |
| 6 (subsquare) | 5′ × 2.5′ | 9.277 km × 4.638 km |
| 8 (extended) | 30″ × 15″ | 927.7 m × 463.8 m |
| 10 | 1.25″ × 0.625″ | 38.65 m × 19.33 m |
Interpreting Linux Memory Metrics
note
Linux memory accounting is full of metrics that look similar but measure very different things. This post is a short tour of the most useful ones and where they come from.
How Linux Models Memory
Linux gives every process a private virtual address space. Pages in that space may or may not be backed by physical RAM at any given moment - the kernel pages them in on demand and evicts them under pressure. Many pages are also shared between processes (libraries, the page cache, copy-on-write forks), so “how much memory does this process use?” does not have a single right answer. It depends on whether you count what is reserved, what is resident, what is private, and how shared pages are split across processes.
On top of that, the kernel itself uses memory for the page cache, slab allocators, buffers, and reclaimable structures. “Free” memory is rarely the number you actually care about, because the kernel will happily use almost all RAM for cache and return it on demand.
Per-Process Metrics
From /proc/<pid>:
- VSZ (Virtual Size): Total size of the process’s virtual address space, including code, data, mapped files, and unallocated reservations. Pages that are mapped but never touched still count. Useful as an upper bound, not as a usage number.
- RSS (Resident Set Size): Amount of physical RAM currently backing the process’s pages. Shared pages are counted in full for every process that maps them, so summing RSS across processes overcounts system memory.
- PSS (Proportional Set Size): Like RSS, but each shared page is divided by the number of processes sharing it. Summing PSS across all processes approximates total physical memory in use.
- USS (Unique Set Size): Private resident memory only - the portion that would actually be freed if the process exited. The truest measure of a process’s incremental cost.
- WSS (Working Set Size): The set of pages a process actively touches over a given time window. This is the minimum RAM the process needs to run without thrashing. Not a static counter; tools like
wss.plsample idle/young page flags to estimate it. - Swap: Pages that have been pushed out to a swap device. Visible per-process in
/proc/<pid>/status(VmSwap) andsmaps.
System-Wide Metrics
From /proc/meminfo:
- MemTotal / MemFree: Total RAM and the small slice that is completely unused.
MemFreeis almost always misleadingly low on a healthy system. - MemAvailable: Kernel’s estimate of how much memory is available for new allocations without swapping, accounting for reclaimable cache and slab. This is the field to watch.
- Buffers / Cached: Page cache used for block devices and file contents. Reclaimable on demand.
- SwapTotal / SwapFree: Swap device capacity and remaining space.
- Dirty / Writeback: Pages modified in cache that still need to be flushed to disk. Spikes here can stall writes.
Pressure and Reclaim
Raw “used vs free” numbers do not tell you whether the system is struggling. Two further signals do:
- PSI (Pressure Stall Information): Exposed at
/proc/pressure/memoryand per-cgroup atmemory.pressuresince kernel 4.20. Reports the percentage of wall time over 10s/60s/300s windows during which some tasks (some) or all tasks (full) were stalled waiting on memory reclaim. Even small non-zero values indicate the system is doing real work to find memory. pgscan/pgsteal/pgmajfaultin/proc/vmstat: Counters for how aggressively the kernel is scanning the LRU lists, reclaiming pages, and servicing major faults from disk. Rising rates here usually precede visible slowdowns.pswpin/pswpoutin/proc/vmstat: Pages swapped in from and out to disk. Non-zeropswpinmeans the system is paying real disk I/O to bring previously-evicted pages back into RAM - a direct indicator that the working set no longer fits.
When to Use Which
- Capacity planning for a single process: USS for incremental cost, WSS for the RAM it actually needs to run without thrashing.
- Adding up processes to explain total RAM: PSS. RSS double-counts shared libraries; USS undercounts them.
- Sizing a container or cgroup memory limit: WSS plus headroom for the page cache the workload depends on. Setting the limit at RSS is a common cause of surprise OOM kills.
- “Is the box about to OOM?": MemAvailable trending down plus PSI
memory.fullabove zero.MemFreealone will mislead you. - Investigating a slowdown that “has plenty of free memory": PSI,
pgscan,pgmajfault, and Dirty/Writeback. High scan rates with low fault rates mean the kernel is working to keep up; high major-fault rates mean it is losing. - Diagnosing a memory leak in a long-running process: watch RSS and USS over time. Rising USS is a real leak; rising RSS with flat USS is usually shared-library or cache growth.
- Comparing two processes that load the same big library: USS, since RSS will charge each one the full library cost.
- Deciding whether to add swap: SwapFree trending toward zero combined with non-trivial PSI
memory.somemeans the system is already paging under pressure. - Tuning a database or file server: Cached and Buffers should be large - that is the page cache doing its job. Worry when Dirty spikes or Writeback stays elevated.
- Choosing what to alert on in production: MemAvailable, PSI
memory.fullavg60, and swap-in rate (pswpininvmstat). These three together catch nearly every memory-related incident before users do.
Further Reading
- PSI - Pressure Stall Information (kernel.org)
- proc_meminfo(5) man page
- PSI: pressure stall information for CPU, memory, and IO (LWN)
- Brendan Gregg - Working Set Size Estimation
- Understanding Memory Metrics: RSS, VSZ, USS, PSS, and Working Sets (Blackwell Systems)
- Process Memory Management in Linux (Baeldung)
- cgroup v2 and Page Cache (Viacheslav Biriukov)
- Getting Started with PSI (Facebook)