81 ways of viewing M87
I want to walk through how an image like this is actually made, because "an astronomical photo" hides a lot of decisions, and I think the decisions are the interesting part.
The data (all public, all real)
Three independent observations of M87 / NGC 4486, each tracing a different physical component:
Band Instrument Source Traces X-ray Chandra / ACIS Chandra openFITS broadband image hot intracluster gas (~10⁷ K), AGN cavities Optical/UV HST ACS/HRC, F330W, prop 9493, 2002-12-10, 300 s, drizzled MAST (via astroquery) nucleus + relativistic jet Radio 1.4 GHz VLA / NVSS NASA SkyView synchrotron jet lobes
Step 1: reprojection onto a common WCS. The three frames have different pixel scales, orientations, and fields of view. They get reprojected (astropy reproject_interp, bicubic) onto a single TAN/gnomonic grid: ICRS J2000, centered on the nucleus, 3 arcmin field, 2048², so 0.088 arcsec/px. After this, the three arrays are pixel-aligned — same sky coordinate at the same (x,y) in all three.
One honest detail: HST's ACS/HRC field is only ~26″, far smaller than the 3′ composite, so HST contributes only the nucleus + jet here. That's a choice, and it's documented.
Step 2: per-band stretch. Astronomical data spans many decades of intensity, so a linear map throws away everything faint. Each band gets a background-percentile subtraction, a percentile clip, and an asinh stretch (gentle on the bright core, generous on faint structure). Parameters differ per band because the noise and dynamic range differ.
Step 3: the 81 recipes. This is the part people ask about. Each recipe is not just a colormap. The pipeline first derives 12 intermediate layers from the aligned data — linear, log, asinh at several strengths, percentile-windowed, and edge-detection stretches. A recipe is then a set of weights that blends those 12 layers into the R, G, B channels, plus a gamma and a saturation. So "Thermal" and "Cyanotype" don't just tint the same picture — they emphasize different structure (one may weight the edge layer into red, another may weight the log layer into blue).
81 recipes × the same aligned data = 81 internally-consistent but visually distinct renderings. None is "the true color." X-rays and radio waves have no color; the mapping is a deliberate, declared choice.
Provenance / reproducibility. Every source file gets a SHA-256 hash; the three hashes are concatenated and hashed again into a single "chain hash" that fingerprints the exact inputs. Recomputing it from the source FITS certifies nothing upstream was silently altered. The output is also saved as a multi-extension FITS (the three reprojected science layers + shared WCS), so anyone can reopen it in DS9 and verify alignment, not just look at a pretty PNG.
Stack: Python — astropy (FITS/WCS), reproject, numpy, scipy.ndimage, matplotlib; astroquery.mast for the HST pull.
Honest disclaimers, up front:
Color is representational, not literal wavelength.
It's a multi-epoch composite (Chandra 2000s, HST 2002, NVSS 1990s), a structural composite, not a single-moment snapshot.
The EHT "donut" is not in here. Its angular scale (~150 µas) is ~6 orders of magnitude smaller than this 3′ field; co-registering them in one image would misrepresent the scales. NVSS has a 45″ beam, so the radio lobes are real but low-resolution.
The takeaway I keep coming back to: there is no single "true" image of a black hole's environment. There's the data, and there's a chain of declared, reproducible choices. The 81 grid is just those choices made visible.
Happy to go deeper on any step!