🌳
pt0/peatsite/beHereNowShadowScriptAI.mjs
1export const beHereNowShadowScript = `
2const rad = Math.PI / 180
3const norm = a => ((a % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI)
4const j2000 = date => date.getTime() / 864e5 + 2440587.5 - 2451545
6const horizontal = (date, lat, lng, ra, dec) => {
7 const d = j2000(date), phi = lat * rad
8 const gmst = norm((280.46061837 + 360.98564736629 * d) * rad)
9 const ha = gmst + lng * rad - norm(ra)
10 const sinAlt = Math.sin(phi) * Math.sin(dec) + Math.cos(phi) * Math.cos(dec) * Math.cos(ha)
11 const elevation = Math.asin(Math.max(-1, Math.min(1, sinAlt)))
12 const azimuth = norm(Math.atan2(Math.sin(ha), Math.cos(ha) * Math.sin(phi) - Math.tan(dec) * Math.cos(phi)) + Math.PI)
13 return { elevation, azimuth }
16const sunPos = (date, lat, lng) => {
17 const d = j2000(date)
18 const L = norm((280.460 + 0.9856474 * d) * rad)
19 const g = norm((357.528 + 0.9856003 * d) * rad)
20 const lambda = L + 1.915 * rad * Math.sin(g) + 0.020 * rad * Math.sin(2 * g)
21 const eps = (23.439 - 0.0000004 * d) * rad
22 const ra = Math.atan2(Math.cos(eps) * Math.sin(lambda), Math.cos(lambda))
23 const dec = Math.asin(Math.sin(eps) * Math.sin(lambda))
24 return horizontal(date, lat, lng, ra, dec)
27const moonPos = (date, lat, lng) => {
28 const d = j2000(date)
29 const mL = norm((218.316 + 13.176396 * d) * rad)
30 const mM = norm((134.963 + 13.064993 * d) * rad)
31 const mF = norm((93.272 + 13.229350 * d) * rad)
32 const lon = mL + 6.289 * rad * Math.sin(mM) - 1.274 * rad * Math.sin(2 * mF - 2 * mM)
33 const beta = 5.128 * rad * Math.sin(mF)
34 const eps = (23.439 - 0.0000004 * d) * rad
35 const ra = Math.atan2(Math.sin(lon) * Math.cos(eps) - Math.tan(beta) * Math.sin(eps), Math.cos(lon))
36 const sinDec = Math.sin(beta) * Math.cos(eps) + Math.cos(beta) * Math.sin(eps) * Math.sin(lon)
37 const dec = Math.asin(Math.max(-1, Math.min(1, sinDec)))
38 const h = horizontal(date, lat, lng, ra, dec)
39 const sunL = norm((280.460 + 0.9856474 * d) * rad)
40 const sunG = norm((357.528 + 0.9856003 * d) * rad)
41 const sunLambda = sunL + 1.915 * rad * Math.sin(sunG)
42 const elong = Math.acos(Math.max(-1, Math.min(1, Math.cos(lon - sunLambda) * Math.cos(beta))))
43 return Object.assign(h, { illumination: (1 - Math.cos(elong)) / 2 })
46const VIEWING_AZ = 315 * rad
48var tzCoords = {
49 'America/New_York': [40.71, -74.01], 'America/Chicago': [41.88, -87.63],
50 'America/Denver': [39.74, -104.99], 'America/Los_Angeles': [34.05, -118.24],
51 'America/Anchorage': [61.22, -149.9], 'America/Sao_Paulo': [-23.55, -46.63],
52 'America/Mexico_City': [19.43, -99.13], 'America/Toronto': [43.65, -79.38],
53 'America/Vancouver': [49.28, -123.12], 'America/Buenos_Aires': [-34.61, -58.38],
54 'Europe/London': [51.51, -0.13], 'Europe/Paris': [48.86, 2.35],
55 'Europe/Berlin': [52.52, 13.4], 'Europe/Madrid': [40.42, -3.7],
56 'Europe/Rome': [41.9, 12.5], 'Europe/Amsterdam': [52.37, 4.9],
57 'Europe/Stockholm': [59.33, 18.07], 'Europe/Moscow': [55.76, 37.62],
58 'Europe/Istanbul': [41.01, 28.98], 'Asia/Tokyo': [35.68, 139.69],
59 'Asia/Shanghai': [31.23, 121.47], 'Asia/Hong_Kong': [22.32, 114.17],
60 'Asia/Singapore': [1.35, 103.82], 'Asia/Dubai': [25.2, 55.27],
61 'Asia/Seoul': [37.57, 126.98], 'Asia/Kolkata': [19.08, 72.88],
62 'Asia/Bangkok': [13.76, 100.5], 'Australia/Sydney': [-33.87, 151.21],
63 'Australia/Perth': [-31.95, 115.86], 'Pacific/Auckland': [-36.85, 174.76],
64 'Africa/Johannesburg': [-26.2, 28.05], 'Africa/Cairo': [30.04, 31.24],
65 'UTC': [0, 0]
68var getCoords = () => {
69 var tz = Intl.DateTimeFormat().resolvedOptions().timeZone
70 var c = tzCoords[tz]
71 if (c) return { lat: c[0], lng: c[1] }
72 var offset = -new Date().getTimezoneOffset() / 60
73 return { lat: 40, lng: offset * 15 }
76var update = () => {
77 var figure = document.querySelector('.be-here-now-figure')
78 if (!figure) return
79 var loc = getCoords()
80 var now = new Date()
81 var sun = sunPos(now, loc.lat, loc.lng)
83 var elevation = Math.max(sun.elevation, 15 * rad), azimuth = sun.azimuth
85 var img = figure.querySelector('.be-here-now-img')
86 var imgH = (img && img.offsetHeight) || 300
87 var shadowLen = imgH / Math.tan(elevation)
88 var darkEnd = Math.min(100, shadowLen / 220 * 100)
90 var relAz = azimuth - VIEWING_AZ
91 var dx = Math.sin(relAz)
92 var dy = Math.max(0.15, Math.cos(relAz))
93 var skew = Math.max(-15, Math.min(15, Math.atan(dx / dy) / rad))
95 var opacity = Math.sin(elevation), r = 0, g = 0, b = 0
96 var warmth = Math.max(0, 1 - elevation / (15 * rad))
97 r = Math.round(50 * warmth); g = Math.round(20 * warmth); b = Math.round(5 * warmth)
98 if (elevation < 2 * rad) opacity *= Math.max(0, elevation) / (2 * rad)
100 figure.style.setProperty('--bh-skew', skew.toFixed(2) + 'deg')
101 figure.style.setProperty('--bh-dark-end', darkEnd.toFixed(1) + '%')
102 figure.style.setProperty('--bh-opacity', opacity.toFixed(3))
103 figure.style.setProperty('--bh-r', r)
104 figure.style.setProperty('--bh-g', g)
105 figure.style.setProperty('--bh-b', b)
108update()
109var img = document.querySelector('.be-here-now-img')
110if (img && !img.complete) img.addEventListener('load', update, { once: true })
111setInterval(update, 60000)
112window.addEventListener('resize', update)
113document.addEventListener('visibilitychange', () => { if (!document.hidden) update() })