ABSTRACT(original article): The Black Hole Explorer (BHEX) is a proposed mission to launch a sub-millimeter radio telescope into Earth orbit that will take the sharpest images in the history of astronomy and reveal novel horizon-scale features of supermassive black holes. Black Hole Vision is an open-source application, freely available on the iOS App Store, that produces lensed images which highlight the key features expected to appear in the black hole images BHEX will capture. The app combines video feeds from the front- and rear-facing iPhone cameras and uses the black hole lensing equations to synthesize an onscreen image displaying the user’s surroundings as if they were gravitationally lensed by a black hole within the cameras’ field of view. Here, we describe how light rays are lensed by non-rotating (Schwarzschild) and rotating (Kerr) black holes, and we list the equations needed for computing black-hole-lensed images. We also describe their specific implementation within Black Hole Vision. CITATION (original article): Roman Berens, Dominic O. Chang, Trevor Gravely, Alexandru Lupsasca (2026), Black Hole Vision: An Interactive iOS Application for Visualizing Black Holes, arXiv:2603.05810. https://doi.org/10.48550/arXiv.2603.05810
Download App: https://wolfr.am/Black-Hole-Vision
GitHub: https://github.com/graveltr/BlackHoleVision/tree/main
Download App: https://wolfr.am/Black-Hole-Vision
GitHub: https://github.com/graveltr/BlackHoleVision/tree/main
Black Hole Vision takes live video feeds from the front and rear facing cameras of the iPhone and simulates the effects of lensing by a rotating (Kerr) black hole. The result is displayed in real-time to the user’s screen.
The user interface is implemented using Swift and the SwiftUI framework. The lensing calculations are done on the GPU via Metal, Apple’s shading (graphics) language.
The code was written at Vanderbilt University by Trevor Gravely with input from Dr. Roman Berens and Prof. Alex Lupsasca. We are grateful to Dominic Chang for sharing his experience with his own lensing software, available on his personal website, https://dominic-chang.com/, or his GitHub, https://github.com/dominic-chang). This project was supported by CAREER award PHY-2340457 and grant AST-2307888 from the National Science Foundation.
Metal implementations of the Jacobi elliptic functions, incomplete elliptic integrals, and complete elliptic integrals are ported from https://www.gnu.org/software/gsl/.
Definitions
Definitions
Screen to Phase Space Mappings
Screen to Phase Space Mappings
◼
textureCoordToPixel
(u, v) ∈ [0,1]²
◼
pixelToScreen
(α, β)
rcrit = 300
base = 0.04
rcrit
◼
αβToηλ
(α, β)
(η, λ)
η(-)+
2
α
2
a
2
cos
θ
o
2
β
λ-αsin
θ
o
◼
normalizeAngle2
(-π,π]
2π
2π
π
◼
FromSphericalCoordinatesCustom
normalizeAngle2
◼
ToSphericalCoordinatesCustom
[0,2π)
Mod
◼
rotateSphericalCoordinate
vo
vs
(n1, n2, n3)
n1
vs
M
In[]:=
Clear[textureCoordToPixel,pixelToScreen,αβToηλ,rotateSphericalCoordinate,FromSphericalCoordinatesCustom,ToSphericalCoordinatesCustom,normalizeAngle2]textureCoordToPixel[textureWidth_,textureHeight_][u_,v_]:=Module[{pixelCoords,center},pixelCoords={u*textureWidth,v*textureHeight};center={textureWidth/2.0,textureHeight/2.0};pixelCoords-center]pixelToScreen[x_,y_]:=Module{base,rcrit,alpha,n,r},base=0.04;rcrit=300;alpha=1000;n=1;r=+;Ifr<rcrit,base*{x,y},+base*{x,y}αβToηλ[a_][θs_][α_,β_]:=Module[{},{(-)*+,-1*α*Sin[θs]}]normalizeAngle2[angle_]:=Module[{a=Mod[angle,2π]},If[a>π,a-2π,a]]FromSphericalCoordinatesCustom[v_]:=Module[{},FromSphericalCoordinates[{v[[1]],v[[2]],normalizeAngle2[v[[3]]]}]]ToSphericalCoordinatesCustom[v_]:=Module[{vSpherical},vSpherical=ToSphericalCoordinates[v];{vSpherical[[1]],vSpherical[[2]],Mod[vSpherical[[3]],2π]}]rotateSphericalCoordinate[vs_,vo_]:=Module[{vsCartesian,voCartesian,zhat,n1,v2,n2,n3,voHatCartesian,M},vsCartesian=FromSphericalCoordinatesCustom[vs];voCartesian=FromSphericalCoordinatesCustom[vo];zhat={0,0,1};n1=vsCartesian/Norm[vsCartesian];v2=zhat-(zhat.n1)*n1;n2=If[Norm[v2]==0,{0,1,0},v2/Norm[v2]];n3=Cross[n2,n1];M={n1,n3,n2};voHatCartesian=M.voCartesian;ToSphericalCoordinatesCustom[voHatCartesian]]
2
x
2
y
1
alpha
n
(r-rcrit)
2
α
2
a
2
Cos[θs]
2
β
Radial Functions and Integrals
Radial Functions and Integrals
Angular Functions and Integrals
Angular Functions and Integrals
Utility
Utility
Define a diagnostic helper that, given a texture coordinate , converts it through the full pipeline — from texture space to pixel coordinates to screen coordinates — and returns the screen α value. Use this to probe and verify the coordinate mappings at any point in the grid, including a boundary correction that nudges away from the exact midpoint to avoid edge ambiguity.
textureToAlpha
(u, v)
v
Clear[textureToAlpha]textureToAlpha[u_,v_]:=Module[{textureWidth,textureHeight,i,j,α,β,η,λ,νθs,cosθf,θf,ϕf,ERRORSTRING,rotatedSphericalCoordinates,ϕfNormalized,oneTwoBdd,threeFourBdd,vp,up,TEXTURE,vCoordSpacing},vCoordSpacing=1/height;vp=If[v==1/2,v+(1/2)vCoordSpacing,v];{i,j}=textureCoordToPixel[1920,1080][u,vp];{α,β}=pixelToScreen[j,i];α]
Computing the Lensing Map
Computing the Lensing Map
Define the core Kerr geodesic integrator kerrLense. Given conserved quantities , an initial polar direction , and observer/source radii, compute the radial roots of the potential, integrate the radial Mino time , then use it to evaluate the final polar angle via CosθObserver and the total azimuth via . Return the source-sphere intercept or an error label if the ray has complex roots or originates inside the horizon.
(η,λ)
νθ
Ir
Iφ+λ·Gφ
(cosθf,φf)
In[]:=
Clear[kerrLense]kerrLense[DEBUG_][a_,M_][η_,λ_][θs_,νθs_,ro_,rs_]:=Module{r1,r2,r3,r4,cosθf,ϕf,ERRORSTRING,IrValue,τ,rplus,cosθo,GϕValue,IϕValue},cosθf=-1.0;ϕf=-1.0;ERRORSTRING="FAILURE";(*Obtaintheroots*){r1,r2,r3,r4}=ObtainRadialRoots[a,M][η,λ];If[DEBUG,Print["r1 = ",r1,", r2 = ",r2,", r3 = ",r3,", r4 = ",r4]];(*Ifanyarecomplexreturn*)If[AnyTrue[{r1,r2,r3,r4},Im[#]!=0&],Return[{-1.0,-1.0,"COMPLEX ROOTS"}]];(*Makesureincorrectsubcaseof(2)*)rplus=M+-;If[r4<rplus,Return[{-1.0,-1.0,"EMITTED FROM BLACK HOLE"}]];IrValue=Ir[r1,r2,r3,r4][ro,rs];If[DEBUG,Print["Ir = ",IrValue]];τ=IrValue;cosθo=CosθObserver[a][η,λ,νθs,θs][τ];If[DEBUG,Print["cos() = ",cosθo]];GϕValue=Gϕ[DEBUG][a][η,λ,θs][τ,νθs];If[DEBUG,Print["Gϕ = ",GϕValue]];IϕValue=Iϕ[a,M][η,λ][r1,r2,r3,r4][ro,rs];If[DEBUG,Print["Iϕ = ",IϕValue]];cosθf=cosθo;ϕf=IϕValue+λ*GϕValue;ERRORSTRING="SUCCESS";{cosθf,ϕf,ERRORSTRING}
2
M
2
a
θ
o
Normalised vertical coordinate of the image's central row as a sanity check on the pixel-to-texture mapping.
Series Expansion
Series Expansion
Texture Export
Texture Export
Export the data to csv
CITE THIS NOTEBOOK
CITE THIS NOTEBOOK
Black Hole Vision: an interactive iOS application for visualizing black holes
by Roman Berens, Dominic O. Chang, Trevor Gravely, Alexandru Lupsasca
Wolfram Community, STAFF PICKS, March 10, 2026
https://community.wolfram.com/groups/-/m/t/3656544
by Roman Berens, Dominic O. Chang, Trevor Gravely, Alexandru Lupsasca
Wolfram Community, STAFF PICKS, March 10, 2026
https://community.wolfram.com/groups/-/m/t/3656544