Apple is working on a Solar wallpaper engine for macOS 27 that groups aerial Landscape wallpapers and automatically switches variants based on the sun's position during the day.
See an updated guide for Beta 6
When the feature flag is enabled, the four Tahoe landscape wallpapers appear under one entry in Settings.
To get the Automatic Tahoe landscape wallpaper, enable the wallpaper feature:
and reboot your Mac. You will no longer see the Golden Gate landscape wallpapers on the list because the new engine reads a macOS 26 wallpaper registry for now. Read below for a more involved process to add the dynamic Golden Gate wallpaper entry.
To go back to the default and bring the Golden Gate landscape wallpapers back:
and reboot your Mac.
Then reboot your Mac.
See an updated guide for Beta 6
When the feature flag is enabled, the four Tahoe landscape wallpapers appear under one entry in Settings.
To get the Automatic Tahoe landscape wallpaper, enable the wallpaper feature:
Bash:
sudo mkdir -p /Library/Preferences/FeatureFlags/Domain && \
sudo defaults write /Library/Preferences/FeatureFlags/Domain/Wallpaper TahoeCombined -dict Enabled -bool true
and reboot your Mac. You will no longer see the Golden Gate landscape wallpapers on the list because the new engine reads a macOS 26 wallpaper registry for now. Read below for a more involved process to add the dynamic Golden Gate wallpaper entry.
To go back to the default and bring the Golden Gate landscape wallpapers back:
Bash:
sudo defaults delete /Library/Preferences/FeatureFlags/Domain/Wallpaper TahoeCombined
and reboot your Mac.
In Beta 4, macOS ships with a macOS 26 wallpaper registry for this combined variants endpoint, so Golden Gate wallpapers don't show by default. Thankfully, you can point the wallpaper engine to a local override file and enable the dynamic switching for Golden Gate wallpapers.
1. Enable the combined-variant wallpaper feature:
2. Create the local catalog directory:
3. Start with Apple’s macOS 27 variant catalog, copying it into your local catalog directory:
4. Add some helpers:
5. Find Landscapes in both catalogs and Golden Gate inside Apple’s Landscapes category:
6. Insert Golden Gate into your local catalog and enable variant combining:
7. Add the Golden Gate Sunset video and its daytime solar coordinate. See at the end for an explanation on how this is calculated:
8. Add the Golden Gate Night video and its nighttime solar coordinate:
9. Verify both records:
The output should show:
and:
10. Point the wallpaper extension at the custom catalog:
11. Force it to use the local catalog:
12. Restart the Mac.
After restarting, Golden Gate should appear as one item and automatically use Sunset during the day and Night after sunset.
1. Enable the combined-variant wallpaper feature:
Bash:
sudo mkdir -p /Library/Preferences/FeatureFlags/Domain && \
sudo defaults write /Library/Preferences/FeatureFlags/Domain/Wallpaper TahoeCombined -dict Enabled -bool true
2. Create the local catalog directory:
Bash:
WALLPAPER_CUSTOM="$HOME/Library/Application Support/com.apple.wallpaper/aerials/custom"
mkdir -p "$WALLPAPER_CUSTOM"
3. Start with Apple’s macOS 27 variant catalog, copying it into your local catalog directory:
Bash:
WALLPAPER_RESOURCES="/System/Library/ExtensionKit/Extensions/WallpaperAerialsExtension.appex/Contents/Resources"
cp "$WALLPAPER_RESOURCES/entries_variants.json" "$WALLPAPER_CUSTOM/entries.json"
4. Add some helpers:
Bash:
LANDSCAPES_ID='A33A55D9-EDEA-4596-A850-6C10B54FBBB5'
GOLDEN_GATE_ID='67512508-D33E-4CBC-8A9E-BE55CEE35C4C'
DYNAMIC_AERIALS_ID='dynamic-aerials'
find_id_index() {
local array_path="$1"
local wanted_id="$2"
local catalog="$3"
local i=0
local current_id
while plutil -extract "$array_path.$i" json -o /dev/null "$catalog" 2>/dev/null; do
current_id=$(plutil -extract "$array_path.$i.id" raw -o - "$catalog" 2>/dev/null || true)
if [[ "$current_id" == "$wanted_id" ]]; then
printf '%s\n' "$i"
return 0
fi
(( i++ ))
done
return 1
}
next_index() {
local array_path="$1"
local catalog="$2"
local i=0
while plutil -extract "$array_path.$i" json -o /dev/null "$catalog" 2>/dev/null; do
(( i++ ))
done
printf '%s\n' "$i"
}
find_asset_index() {
local shot_id="$1"
local catalog="$2"
local i=0
local value
while plutil -extract "assets.$i" json -o /dev/null "$catalog" 2>/dev/null; do
value=$(plutil -extract "assets.$i.shotID" raw -o - "$catalog" 2>/dev/null || true)
if [[ "$value" == "$shot_id" ]]; then
printf '%s\n' "$i"
return 0
fi
(( i++ ))
done
return 1
}
next_asset_index() {
local catalog="$1"
local i=0
while plutil -extract "assets.$i" json -o /dev/null "$catalog" 2>/dev/null; do
(( i++ ))
done
printf '%s\n' "$i"
}
install_solar_asset() {
local shot_id="$1"
local altitude="$2"
local azimuth="$3"
local source_index
local target_index
local asset_json
local variant_json
if ! target_index=$(find_asset_index "$shot_id" "$WALLPAPER_CUSTOM/entries.json"); then
source_index=$(find_asset_index "$shot_id" "$WALLPAPER_RESOURCES/entries.json") || {
echo "Could not find $shot_id in Apple’s catalog." >&2
return 1
}
asset_json=$(
plutil -extract "assets.$source_index" json -o - \
"$WALLPAPER_RESOURCES/entries.json"
) || return 1
target_index=$(next_asset_index "$WALLPAPER_CUSTOM/entries.json")
plutil -insert "assets.$target_index" -json "$asset_json" \
"$WALLPAPER_CUSTOM/entries.json" || return 1
fi
variant_json="{\"solar\":{\"altitude\":$altitude,\"azimuth\":$azimuth}}"
if plutil -extract "assets.$target_index.variant" json -o /dev/null \
"$WALLPAPER_CUSTOM/entries.json" 2>/dev/null; then
plutil -replace "assets.$target_index.variant" -json "$variant_json" \
"$WALLPAPER_CUSTOM/entries.json"
else
plutil -insert "assets.$target_index.variant" -json "$variant_json" \
"$WALLPAPER_CUSTOM/entries.json"
fi
}
5. Find Landscapes in both catalogs and Golden Gate inside Apple’s Landscapes category:
Bash:
SOURCE_LANDSCAPES_INDEX=$(
find_id_index categories "$LANDSCAPES_ID" \
"$WALLPAPER_RESOURCES/entries.json"
) || {
echo "Could not find Landscapes in Apple’s catalog."
exit 1
}
CUSTOM_LANDSCAPES_INDEX=$(
find_id_index categories "$LANDSCAPES_ID" \
"$WALLPAPER_CUSTOM/entries.json"
) || {
echo "Could not find Landscapes in the custom catalog."
exit 1
}
SOURCE_GOLDEN_GATE_INDEX=$(
find_id_index \
"categories.$SOURCE_LANDSCAPES_INDEX.subcategories" \
"$GOLDEN_GATE_ID" \
"$WALLPAPER_RESOURCES/entries.json"
) || {
echo "Could not find Golden Gate in Apple’s catalog."
exit 1
}
SOURCE_DYNAMIC_AERIALS_INDEX=$(
find_id_index categories "$DYNAMIC_AERIALS_ID" \
"$WALLPAPER_RESOURCES/entries.json"
) || {
echo "Could not find Apple’s graphical macOS wallpaper category."
exit 1
}
6. Insert Golden Gate into your local catalog and enable variant combining:
Bash:
if CUSTOM_GOLDEN_GATE_INDEX=$(
find_id_index \
"categories.$CUSTOM_LANDSCAPES_INDEX.subcategories" \
"$GOLDEN_GATE_ID" \
"$WALLPAPER_CUSTOM/entries.json"
); then
echo "Golden Gate is already in the custom Landscapes category."
else
CUSTOM_GOLDEN_GATE_INDEX=$(
next_index \
"categories.$CUSTOM_LANDSCAPES_INDEX.subcategories" \
"$WALLPAPER_CUSTOM/entries.json"
)
GOLDEN_GATE_JSON=$(
plutil -extract \
"categories.$SOURCE_LANDSCAPES_INDEX.subcategories.$SOURCE_GOLDEN_GATE_INDEX" \
json -o - \
"$WALLPAPER_RESOURCES/entries.json"
)
plutil -insert \
"categories.$CUSTOM_LANDSCAPES_INDEX.subcategories.$CUSTOM_GOLDEN_GATE_INDEX" \
-json "$GOLDEN_GATE_JSON" \
"$WALLPAPER_CUSTOM/entries.json"
fi
GOLDEN_GATE_PATH="categories.$CUSTOM_LANDSCAPES_INDEX.subcategories.$CUSTOM_GOLDEN_GATE_INDEX"
if plutil -extract "$GOLDEN_GATE_PATH.combineVariants" raw -o - \
"$WALLPAPER_CUSTOM/entries.json" >/dev/null 2>&1; then
plutil -replace "$GOLDEN_GATE_PATH.combineVariants" -bool true \
"$WALLPAPER_CUSTOM/entries.json"
else
plutil -insert "$GOLDEN_GATE_PATH.combineVariants" -bool true \
"$WALLPAPER_CUSTOM/entries.json"
fi
if CUSTOM_DYNAMIC_AERIALS_INDEX=$(
find_id_index categories "$DYNAMIC_AERIALS_ID" \
"$WALLPAPER_CUSTOM/entries.json"
); then
echo "The graphical macOS wallpaper category is already installed."
else
DYNAMIC_AERIALS_JSON=$(
plutil -extract \
"categories.$SOURCE_DYNAMIC_AERIALS_INDEX" \
json -o - \
"$WALLPAPER_RESOURCES/entries.json"
)
plutil -insert categories.0 \
-json "$DYNAMIC_AERIALS_JSON" \
"$WALLPAPER_CUSTOM/entries.json"
fi
for SHOT_ID in GG_LM_H GG_LM_V GG_DM_H GG_DM_V; do
if find_asset_index "$SHOT_ID" \
"$WALLPAPER_CUSTOM/entries.json" >/dev/null; then
echo "$SHOT_ID is already installed."
continue
fi
SOURCE_ASSET_INDEX=$(
find_asset_index "$SHOT_ID" \
"$WALLPAPER_RESOURCES/entries.json"
) || {
echo "Could not find $SHOT_ID in Apple’s catalog."
exit 1
}
TARGET_ASSET_INDEX=$(
next_asset_index "$WALLPAPER_CUSTOM/entries.json"
)
ASSET_JSON=$(
plutil -extract "assets.$SOURCE_ASSET_INDEX" \
json -o - \
"$WALLPAPER_RESOURCES/entries.json"
)
plutil -insert "assets.$TARGET_ASSET_INDEX" \
-json "$ASSET_JSON" \
"$WALLPAPER_CUSTOM/entries.json"
done
CATEGORY_COUNT=$(
next_index categories "$WALLPAPER_CUSTOM/entries.json"
)
for (( CATEGORY_INDEX=0; CATEGORY_INDEX<CATEGORY_COUNT; CATEGORY_INDEX++ )); do
CATEGORY_PATH="categories.$CATEGORY_INDEX.preferredOrder"
if plutil -extract "$CATEGORY_PATH" raw -o - \
"$WALLPAPER_CUSTOM/entries.json" >/dev/null 2>&1; then
plutil -replace "$CATEGORY_PATH" -integer "$CATEGORY_INDEX" \
"$WALLPAPER_CUSTOM/entries.json"
else
plutil -insert "$CATEGORY_PATH" -integer "$CATEGORY_INDEX" \
"$WALLPAPER_CUSTOM/entries.json"
fi
done
7. Add the Golden Gate Sunset video and its daytime solar coordinate. See at the end for an explanation on how this is calculated:
Bash:
install_solar_asset GG_A_SUNSET 35 180
8. Add the Golden Gate Night video and its nighttime solar coordinate:
Bash:
install_solar_asset GG_A_NIGHT -35 180
9. Verify both records:
Bash:
SUNSET_INDEX=$(find_asset_index GG_A_SUNSET "$WALLPAPER_CUSTOM/entries.json")
plutil -extract "assets.$SUNSET_INDEX.variant" json -o - "$WALLPAPER_CUSTOM/entries.json"
Bash:
NIGHT_INDEX=$(find_asset_index GG_A_NIGHT "$WALLPAPER_CUSTOM/entries.json")
plutil -extract "assets.$NIGHT_INDEX.variant" json -o - "$WALLPAPER_CUSTOM/entries.json"
The output should show:
JSON:
{"solar":{"altitude":35,"azimuth":180}}
and:
JSON:
{"solar":{"altitude":-35,"azimuth":180}}
10. Point the wallpaper extension at the custom catalog:
Bash:
defaults write com.apple.wallpaper.aerial AerialManifestLocalPathOverride -string "$WALLPAPER_CUSTOM/entries.json"
11. Force it to use the local catalog:
Bash:
defaults write com.apple.wallpaper.aerial AerialManifestForceLocal -bool true
12. Restart the Mac.
After restarting, Golden Gate should appear as one item and automatically use Sunset during the day and Night after sunset.
Bash:
defaults delete com.apple.wallpaper.aerial AerialManifestLocalPathOverride
Bash:
defaults delete com.apple.wallpaper.aerial AerialManifestForceLocal
Bash:
rm -rf -- "$HOME/Library/Application Support/com.apple.wallpaper/aerials/custom"
Bash:
sudo defaults delete /Library/Preferences/FeatureFlags/Domain/Wallpaper TahoeCombined
Then reboot your Mac.
Apple’s Tahoe dynamic wallpaper includes four variants: Morning, Day, Evening, and Night. Apple assigns each variant a pair of solar coordinates:
The Golden Gate landscape set only has two variants. We therefore use:
If you'd like to use other solar positions, use the following Python script to estimate times based on coordinates:
This should print:
altitude: how high the Sun is above or below the horizon.azimuth: the Sun’s compass direction, where180°means south.
The Golden Gate landscape set only has two variants. We therefore use:
- Golden Gate Sunset:
{35, 180} - Golden Gate Night:
{-35, 180}
35° above the horizon and the other is 35° below it. Because both use the same azimuth, macOS considers them equally close when the Sun is at approximately 0° altitude, i.e. the horizon. That makes WallpaperAgent switch to the Sunset variant around sunrise and to Night around sunset.If you'd like to use other solar positions, use the following Python script to estimate times based on coordinates:
Bash:
DAY='35,180'
NIGHT='-35,180'
LAT=37.3230; LON=-122.0322
DATE='2026-08-02'; UTC_OFFSET=-7
python3 -c 'exec("""import sys,math,datetime
day=tuple(map(float,sys.argv[1].split(",")));night=tuple(map(float,sys.argv[2].split(",")));lat=float(sys.argv[3]);lon=float(sys.argv[4]);date=datetime.date.fromisoformat(sys.argv[5]);tz=float(sys.argv[6])
def sun(m):
h=m/60;n=date.timetuple().tm_yday;g=2*math.pi/365*(n-1+(h-12)/24);eq=229.18*(.000075+.001868*math.cos(g)-.032077*math.sin(g)-.014615*math.cos(2*g)-.040849*math.sin(2*g));dec=.006918-.399912*math.cos(g)+.070257*math.sin(g)-.006758*math.cos(2*g)+.000907*math.sin(2*g)-.002697*math.cos(3*g)+.00148*math.sin(3*g);tst=(m+eq+4*lon-60*tz)%1440;ha=math.radians(tst/4-180);p=math.radians(lat);cz=max(-1,min(1,math.sin(p)*math.sin(dec)+math.cos(p)*math.cos(dec)*math.cos(ha)));e=90-math.degrees(math.acos(cz))
if e>85:r=0
elif e>5:t=math.tan(math.radians(e));r=(58.1/t-.07/t**3+.000086/t**5)/3600
elif e>-.575:r=(1735+e*(-518.2+e*(103.4+e*(-12.79+e*.711))))/3600
else:r=-20.772/math.tan(math.radians(e))/3600
return e+r,(math.degrees(math.atan2(math.sin(ha),math.cos(ha)*math.sin(p)-math.tan(dec)*math.cos(p)))+180)%360
def da(a,b):return (a-b+180)%360-180
def f(m):
a,z=sun(m);return (a-day[0])**2+da(z,day[1])**2-(a-night[0])**2-da(z,night[1])**2
def fmt(m):
h=int(m//60)%24;mi=int(m%60);s=round(m%1*60)
if s==60:mi+=1;s=0
if mi==60:h=(h+1)%24;mi=0
return f"{h%12 or 12}:{mi:02d}:{s:02d} {\x27AM\x27 if h<12 else \x27PM\x27}"
step=.25;prev=f(0)
for i in range(1,5761):
x=i*step;cur=f(x)
if prev*cur<0:
lo=x-step;hi=x
for _ in range(40):
mid=(lo+hi)/2
if f(lo)*f(mid)<=0:hi=mid
else:lo=mid
t=(lo+hi)/2;print(("Night -> Day" if f(t-.1)>0 else "Day -> Night")+": "+fmt(t))
prev=cur
""")' "$DAY" "$NIGHT" "$LAT" "$LON" "$DATE" "$UTC_OFFSET"
This should print:
Code:
Night -> Day: 6:13:58 AM
Day -> Night: 8:14:41 PM
Last edited: