Skip to content

Instantly share code, notes, and snippets.

@seangeleno
Created October 13, 2019 19:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seangeleno/52e30c638c9ad83ed0f422f5cdf99956 to your computer and use it in GitHub Desktop.
Save seangeleno/52e30c638c9ad83ed0f422f5cdf99956 to your computer and use it in GitHub Desktop.
Visualizing The Sun's Path in the Sky
<canvas id="zdog-canvas" width="800" height="640"></canvas>
<div class="place-controls">
<button id="south">« South</button>
<p id="place">Current Place</p>
<button id="north">North »</button>
</div>
<div class="time-controls">
<p id="month">Current Month</p>
<div class="buttons">
<button id="rewind">«</button>
<button id="pause">||</button>
<button id="play" class="hidden">&gt;</button>
<button id="forward">»</button>
</div>
</div>
const { cos, PI, ceil, random } = Math
const deg = degrees => {
return PI / 180 * degrees
}
const maybeNegative = number => {
return random() >= .5 ? -number : number
}
const times = (howMany, what) => {
return [...Array(howMany).keys()].map(what)
}
const query = selector =>
selector[0] === '#'
? document.getElementById(selector.slice(1))
: document.querySelectorAll(selector)
const getPlace = (placeList, index) => {
const { name, country, lat } = placeList[index]
const nameAndCountry = [ name, country ].filter(Boolean)
const hemisphere = lat > 0 ? 'N' : lat < 0 ? 'S' : ''
return {
name: `${nameAndCountry.join(', ')} (${lat}°${hemisphere})`,
lat
}
}
const calculateMonth = (monthList, time) => {
const monthLength = 1 / 6
const monthIndex = time / monthLength
return monthList[ceil(monthIndex) % 12]
}
const places = [
{ name: 'South Pole', country: null, lat: -90 },
{ name: 'Vostok', country: 'Antarctica', lat: -75 },
{ name: 'Falkland Islands', country: 'UK', lat: -51 },
{ name: 'Christchurch', country: 'New Zealand', lat: -43 },
{ name: 'São Paulo', country: 'Brazil', lat: -23 },
{ name: 'Kampala', country: 'Uganda', lat: 0 },
{ name: 'Dubai', country: 'UAE', lat: 23 },
{ name: 'Istanbul', country: 'Turkey', lat: 41 },
{ name: 'Helsinki', country: 'Finland', lat: 60 },
{ name: 'Tromsø', country: 'Norway', lat: 69 },
{ name: 'Alert', country: 'Canada', lat: 82 },
{ name: 'North Pole', country: null, lat: 90 },
]
const months = [
'June',
'July',
'August',
'September',
'October',
'November',
'December',
'January',
'February',
'March',
'April',
'May'
]
const daySpeeds = {
paused: 0,
playing: 0.01,
rewinding: -0.01,
forwarding: 0.1
}
const maxAscension = .23
const state = {
place: 8,
mode: 'playing',
season: 0 // 0 = june, 1 = december
}
const drawing = new Zdog.Illustration({
element: '#zdog-canvas',
dragRotate: true,
zoom: 500,
rotate: { x: -deg(25) }
})
const ground = new Zdog.Cylinder({
addTo: drawing,
diameter: 1,
length: .05,
stroke: false,
color: 'forestgreen',
frontFace: 'yellowgreen',
backface: 'rgb(30, 100, 10)',
rotate: { x: deg(90) }
})
const clouds = []
const addRandomCloud = (parent, x) => {
const color = 'rgba(230, 240, 255, .2)'
const z = maybeNegative(random() / 2)
clouds.push(new Zdog.Box({
addTo: parent,
width: .001,
height: .0025,
depth: .001,
stroke: .1,
color,
translate: {
x,
y: -.3,
z
}
}))
clouds.push(new Zdog.Box({
addTo: parent,
width: .001,
height: .003,
depth: .001,
stroke: .15,
color,
translate: {
x: x + .1,
y: -.3,
z
}
}))
if (random() > .75) {
clouds.push(new Zdog.Box({
addTo: parent,
width: .001,
height: .0025,
depth: .001,
stroke: .1,
color,
translate: {
x: x + .2,
y: -.3,
z
}
}))
if (random() > .5) {
clouds.push(new Zdog.Box({
addTo: parent,
width: .0005,
height: .0005,
depth: .0005,
stroke: .07,
color,
translate: {
x: x + .27,
y: -.3,
z
}
}))
}
}
}
const sunTiltPlane = new Zdog.Anchor({
addTo: drawing,
})
const sunOrbitPlane = new Zdog.Anchor({
addTo: sunTiltPlane
})
const sunAscensionPlane = new Zdog.Anchor({
addTo: sunOrbitPlane,
})
const sun = new Zdog.Shape({
addTo: sunAscensionPlane,
stroke: .1,
color: '#fd5',
translate: { y: -.5 }
})
let cloudX = 0
let cloudBoost = 2.5
const loop = () => {
const speed = daySpeeds[state.mode]
const place = getPlace(places, state.place)
const tilt = deg(-place.lat)
if (sunTiltPlane.rotate.z !== tilt) {
sunTiltPlane.rotate.z += ((tilt - sunTiltPlane.rotate.z) / 5)
}
sunOrbitPlane.rotate.x += speed
sunAscensionPlane.translate.x = (
cos(PI * state.season) * maxAscension
)
state.season += (speed / 180)
if (random() < speed * cloudBoost) {
cloudX += ((.6 - cloudX) / 10)
cloudBoost -= ((cloudBoost - .5) / 10)
addRandomCloud(drawing, cloudX)
}
clouds.forEach(cloud => {
if (cloud) {
cloud.translate.x -= (speed / 10)
if (cloud.translate.x < -3) {
cloud.remove()
cloud = null
}
}
})
query('#place').innerText = place.name
const month = calculateMonth(months, state.season)
query('#month').innerText = month
drawing.updateRenderGraph()
requestAnimationFrame(loop)
}
loop()
const onClickSouth = () => {
state.place--
query('#north').removeAttribute('disabled')
if (!places[state.place - 1]) {
query('#south').setAttribute('disabled', true)
}
}
const onClickNorth = () => {
state.place++
query('#south').removeAttribute('disabled')
if (!places[state.place + 1]) {
query('#north').setAttribute('disabled', true)
}
}
const playPause = () => {
if (state.mode !== 'paused') {
state.savedMode = state.mode
state.mode = 'paused'
} else {
state.mode = state.savedMode
}
query('#play').classList.toggle('hidden')
query('#pause').classList.toggle('hidden')
}
const toggleRewind = () => {
state.mode = state.mode !== 'rewinding'
? 'rewinding'
: 'playing'
}
const toggleForward = () => {
state.mode = state.mode !== 'forwarding'
? 'forwarding'
: 'playing'
}
query('#south').addEventListener('click', onClickSouth)
query('#north').addEventListener('click', onClickNorth)
query('#play').addEventListener('click', playPause)
query('#pause').addEventListener('click', playPause)
query('#rewind').addEventListener('click', toggleRewind)
query('#forward').addEventListener('click', toggleForward)
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
html, body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
body {
font: normal 100%/1 "Operator Mono", monospace;
}
button {
appearance: none;
border: none;
background: #000;
color: #fff;
padding: .3em;
min-width: 32px;
font: inherit;
&:disabled {
background: gray;
}
}
.hidden {
display: none;
}
#zdog-canvas {
cursor: move;
}
@media (max-width: 800px) {
#zdog-canvas {
width: 100%;
}
}
.place-controls {
position: absolute;
top: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
max-width: 800px;
button {
padding: .5em;
}
#place {
font-size: 1.2em;
}
}
@media (max-height: 640px) {
.place-controls {
top: 0;
}
}
.time-controls {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
.buttons {
display: flex;
}
button:not(:last-child) {
margin-right: 1em;
}
#month {
font-size: 1.2em;
padding-bottom: .25em;
text-align: center;
}
}
@media (max-height: 640px) {
.time-controls {
bottom: 0;
}
}

Visualizing The Sun's Path in the Sky

Change location using the south/north buttons to see latitude's effect on sun's path in the sky.

Rewind, pause or fast-forward using the buttons below.

A Pen by Mustafa Enes on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment