This project aims to develop tools for simulating solar shadows and predicting the paths of celestial objects using the Wolfram Language. Our primary objective was to simulate shadows cast by the sun at various dates and geographic latitudes using shadow maps and ray tracing of simple 3D scenes. The two main functions we wrote are sunShadowPlot, which visualizes sun-cast shadows across surfaces, and skyPathPlot, which maps the historical and future trajectories of celestial bodies. These tools rely on AstroPosition, Graphics3D, and ray casting methods, with prospective applications in solar site planning, daylight modeling for sustainable architecture, and photovoltaic system optimization.
​
I look forward to further developing this work outside of the Wolfram Summer School, particularly in the context of real-world solar planning and energy engineering. Your thoughts, suggestions, etc. are welcome!

Introduction

This project explores how to simulate solar shadows and visualize the paths of celestial objects using the Wolfram Language. Motivated by applications in sustainable design and solar energy modeling, we developed two key functions, sunShadowPlot and skyPathPlot, that make use of
AstroPosition
and
Graphics3D
alongside simple ray casting and shadow mapping techniques. The tools allow users to trace the sun’s rays across 3D geometry to estimate shadows cast at any given date and location, and to track the historical and future trajectories of celestial bodies across the sky.
The idea was simple: Can we create compact and customizable tools to answer:
◼
  • Where will the sun
    be
    ?
  • ◼
  • How will its movement affect shadows over time?
  • By building these functions from the ground up and visualizing the results in 3D, we gained insight into both solar geometry and real-world energy applications. In the sections below, I share how the tools were built, what they look like in action, and what I hope to do next with them.

    Implementation description and discussion

    Overview

    Two supporting Mathematica tools (skyPathPlot and sunShadowPlot) are used for the project implementation. Both are developed on top of Mathematica’s astronomical calculation tools and make heavy use of the
    AstroPosition
    function. The skyPathPlot function computes the horizontal coordinates (South, East, Up) of the sun, or other celestial bodies relative to a geographic location on earth and a provided time, and then draws their paths on a virtual dome representing the sky. The function was a byproduct of the project, whose main goals concerned shadow mapping. The sunShadowPlot function reuses part of this functionality to compute the ray from the sun to the centre of a small 3D scene located at a specified location and time. The scene is assumed to be a plane, and defaults to a small house. The function then generates a set of parallel rays to the sun-origin ray, and tests for intersections between these rays and the scene to estimate the shadows cast by the house on the surrounding space.

    Projecting celestial object positions onto the local sky hemisphere

    Projecting points onto the unit sphere relative to the origin

    Both skyPathPlot and sunShadowPlot rely on the ability to project the positions of celestial objects onto a local sky hemisphere given a location and date. To do this, we assumed the hemisphere to be the top half of a unit-sphere, and wrote a simple function that, given a list of points in 3D space, computes the half-lines from these points to the origin, and then finds the intersection between these half-lines and the unit sphere. Given a collection of points representing the relative positions of astronomical entities with regard to the horizontal plane at a position on earth and a date, the function will return the list of positions of these objects on the sky hemisphere.
    Define a helper function to project points onto the unit sphere based on the intersection of the half lines from the origin through these points:
    In[]:=
    ClearAll[projectPointsToUnitSphere]​​projectPointsToUnitSphere[points_List]:=Block[{x,y,z},Map[​​First[NSolveValues[{{x,y,z}∈#,{x,y,z}∈Sphere[]},{x,y,z}]]&,​​(Map[HalfLine[#]&,Activate[Thread[{Inactive[{0,0,0}],(points)}]]])]]/;AllTrue[points,ListQ]​​ ​​projectPointsToUnitSphere[point_List]:=First[projectPointsToUnitSphere[{point}]]
    Example:
    In[]:=
    With[{points=RandomPoint[Sphere[{0,0,0},.2],10]},​​Graphics3D[{​​Opacity[.3],Sphere[],PointSize[.05],​​(*Startingpoints:*)Orange,Point[points],​​(*Half-lines:*)Black,HalfLine/@Thread[{points,projectPointsToUnitSphere[points]}],​​(*Projectedpoints:*)Darker[Green],Point[projectPointsToUnitSphere[points]]}]]
    Out[]=

    Getting horizontal celestial positions of celestial bodies

    Armed with this projection tool, the next thing we need is to define a function that will retrieve the position on the sky dome of a particular celestial body in Cartesian horizontal coordinates relative to a position and time.
    Define a function to retrieve the position of a celestial body in the sky from a particular point on earth taken as the origin in Cartesian coordinates, where the position is projected onto the unit sphere:
    ClearAll[celestialBodySkyPosition]​​celestialBodySkyPosition[celestialBody_,location_:Here,time_:Now]:=projectPointsToUnitSphere[​​AstroPosition[celestialBody,{"Horizon","Location"->location,"Date"->time},"Cartesian"]["Data"]]
    Example:
    In[]:=
    celestialBodySkyPosition
    Sun
    STAR
    ,Here,Now
    Out[]=
    {-0.770294,-0.572147,-0.281594}
    We can make use of this function in defining skyPathPlot:

    Plotting trajectories of celestial objects in the day/night sky

    Define a function to plot the positions or trajectories of celestial bodies in the sky from a particular point on earth, and provided a time or time interval:
    ClearAll[skyPathPlot]​​Options[skyPathPlot]={"CelestialBodyColors"->Automatic};​​skyPathPlotcelestialBodies_List:
    Sun
    STAR
    ,location_:Here,{startTime_,endTime_,increment_},scene_List:
    ,OptionsPattern[]:=Module​​ {astroPositions,bodyColors,nightSkyTexture},​​ ​​ (*Collectthetrajectoriesoftherequestedcelestialbodiesoverthespecifiedtimeinterval:*)​​ astroPositions=AssociationThreadcelestialBodies,Table​​ AstroPosition[celestialBody,{"Horizon","Location"->location,"Date"->time},"Cartesian"],​​ {celestialBody,celestialBodies},{time,DateRange[startTime,endTime,increment]};​​ ​​ (*Pickcolorsforthecelestialbodiesandtheirtrajectories:*)​​ bodyColors=If[OptionValue["CelestialBodyColors"]===Automatic,Map[ColorData[98],Range[Length[celestialBodies]]],OptionValue["CelestialBodyColors"]];​​ ​​ (*Producethefinalplot:*)​​ LabeledGraphics3D​​ (*Cardinaldirections:*)​​ Inset["North",1.2{-1,0,0}],Inset["South",1.2{1,0,0}],Inset["East",1.2{0,1,0}],Inset["West",1.2{0,-1,0}],​​ (*Skyhemisphere:*)​​ Opacity[.3],Ball[{0,0,0},1],​​ (*House:*)​​ Opacity[1],Brown,scene,​​ (*Celestialbodytrajectories:*)​​ Thread[{bodyColors,Values[Map[Line,Map[projectPointsToUnitSphere[#["Data"]]&,astroPositions,{2}]]]}],​​ (*Celestialbodies:*)​​ Thread[{bodyColors,Values[Map[Ball[projectPointsToUnitSphere[Last[#]["Data"]],.025]&,astroPositions]]}]​​ ,PlotRange->{All,All,{0,All}},Boxed->False,PlotRangePadding->None,ImagePadding->None,​​ SwatchLegend[bodyColors,celestialBodies],Right​​
    The function uses AstroPosition to retrieve locations of celestial bodies over the specified time range. It then projects these positions onto the unit hemisphere and plots them as trajectories on the sky dome. The latest positions of the bodies are represented by balls. Depending on the specified bodies requested, some will be fully visible, and others will not.
    Example:
    In[]:=
    skyPathPlot​​
    Sun
    STAR
    ,
    Moon
    PLANETARY MOON
    ,
    Mercury
    PLANET
    ,
    Venus
    PLANET
    ,
    Mars
    PLANET
    ,
    Jupiter
    PLANET
    ,
    Saturn
    PLANET
    ,
    Uranus
    PLANET
    ,
    Neptune
    PLANET
    ,​​Here,Now-
    1.5
    day
    ,Now,
    1
    h
    ,​​"CelestialBodyColors"->{
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    }
    Out[]=
    Sun
    Moon
    Mercury
    Venus
    Mars
    Jupiter
    Saturn
    Uranus
    Neptune
    The output here is A 3D model of the local view of the night sky alongside trails representing the trajectories of the tracked celestial bodies.

    Estimating whether a light ray casts a shadow

    To move on, we need a way to decide whether a particular beam of light comes into contact with any part of our 3D scene (think of a house or other objects). This intersection test is fundamental for simulating realistic behavior of sunlight: if a ray leaving the Sun and pointing to a certain point on the ground is blocked by an object, that point is in shadow. If, however, the ray passes unhindered, the point is lit. By repeating this test over a grid of rays covering the ground in the scene, we can classify positions on the surface of the local plane as lit or in shadow.
    Let’s define a function to compute intersections between rays (Line or HalfLine objects) and simple lists of 3D geometric primitives.
    Define a helper function to check for intersections between a ray and a list of 3D geometry primitives:
    Example:

    Rudimentary raster shadow casting

    Given the rayIntersectsQ tool defined above, we can produce an image representing lit or shaded areas by casting and computing the intersections between a grid of rays and the local geometry. Let’s define a simple function to compute the shadow map of our scene given a date and location.
    Define a function to produce an image representing the shadows on the ground plane, provided a scene made of 3D geometry primitives:
    The output of this function is a binary shadow mask in which each pixel indicates whether a corresponding point on the ground is in direct sunlight, or is occluded.
    The process proceeds as follows:
    ◼
  • The function computes the position of the sun relative to the origin in the scene using AstroPosition, and generates a sun-origin ray.
  • ◼
  • The function then generates a grid of parallel rays to the sun-origin ray and casts them over the scene.
  • ◼
  • If the rays intersect with the scene geometry, they indicate that the corresponding location on the plane is in shadow. Otherwise, it is considered lit. The results of testing for intersections between these rays and the local scene geometry are interpreted as an image.
  • We can use this function to generate a top-view animation of the shadow cast by the sun over our scene over any span of time, with the scene located anywhere on Earth.
    Example:

    Estimating shadows cast by the sun on a given date and location

    We need to come up with a way to synthesize a full 3D visualization of solar shadows based on real-world astronomical data. The function should take an input location and date, as well as a 3D scene composed of simple 3D geometric primitives, such as cuboids and pyramids for buildings or objects. Given these data, the function will compute how sun rays would interact with scene geometry, identifying occluded regions and generating a realistic ground shadow projection.
    Define a function to plot the position of the sun in the sky at a date and location, and the shadow it will cast given a scene made of 3D geometry primitives:
    The process implemented in the function above roughly follows these next steps:
    ◼
  • The function computes the position of the sun relative to the origin in the scene using AstroPosition, and generates a sun-origin ray.
  • ◼
  • The function then generates a grid of parallel rays to the sun-origin ray and casts them over the scene.
  • ◼
  • If the rays intersect with the scene geometry, they indicate that the corresponding location on the plane is in shadow. Otherwise, it is considered lit.
  • ◼
  • The results of testing for intersections between these rays and the local scene geometry are interpreted either as a point cloud or as an image, and represented in a 3D reconstruction of the local scene.
  • ◼
  • The scene is constructed using Graphics3D.
  • We can now use the function to determine what the shadows cast on a given date and location would look like, given a local scene.
    Here is the point cloud shadow representation:
    Example:
    Here is the raster representation:
    Example:

    Reflections on the process

    Getting the project to this point has been both intellectually stimulating and technically challenging. One of the most demanding aspects was devising an efficient ray-based shadow estimation within Mathematica’s symbolic framework, balancing visual clarity with computational performance. This challenge pushed me to learn deeply about geometric transformations, spatial reasoning, and 3D visualization in Wolfram Language. It was extremely rewarding to feed astronomical data into a manipulable 3D space and see something as abstract as solar lighting angles translated into tangible shadow patterns.

    Acknowledgements

    In particular, my best thanks to my advisor Philéas Dazeley-Gaist, for the patience, technical advice, and support along the completion of this work. Their consideration and willingness to help me make sense of every step of the process was truly invaluable, and I am so grateful to have been able to work under their guidance.
    And I’m also so grateful to Christopher Wolfram and John McNally, who graciously spent hours of their time and expertise helping me unravel problems and refine my ideas. Most of the time, their suggestions were the Aha! moments I was looking for.
    And let me thank once again Stephen Wolfram, without whose vision program like this wouldn’t exist. And thank you for suggesting the project direction, and for helping me take the time to sit and reflect and think more deeply about where I want my intellectual passions to be. It has been really, truly helpful in clarifying a path that I am really excited to continue.
    I would like to express my heartfelt gratitude to everyone involved in making the 2025 Wolfram Summer School a truly transformative experience.

    Sources

    ◼
  • “Position of the Sun.” Wikipedia, Wikimedia Foundation, 16 Apr. 2025, https://en.wikipedia.org/wiki/Position_of_the_Sun.
  • ◼
  • “Azimuth.” Wikipedia, Wikimedia Foundation, 23 June 2025, https://en.wikipedia.org/wiki/Azimuth.
  • Cite This Notebook

    Mapping Solar Shadows and Celestial Objects in the Sky​
    by Amaad Rafi Zaidi​
    Wolfram Community, STAFF PICKS, 9 July, 2025
    ​https://community.wolfram.com/groups/-/m/t/3496335