cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              menuBg = rgba(255,255,255,0.5)
highlightBg = rgba(255,255,255,0.85)
bigMrg = .5em
lilMrg = .25em

html{overflow:hidden}

#toolbar
  position fixed
  background menuBg
  left 0
  top 0
  max-height 100%
  overflow-y auto
  padding bigMrg
  padding-bottom 0
  font 10pt sans-serif
  
nav  
  margin-bottom bigMrg
  background menuBg
  padding lilMrg
  
nav:not(:first-child)
  transition all .5s ease-in-out
  overflow hidden
  
nav:not(:first-child):not(:last-child)
  height 4em
  
h2
  margin-bottom lilMrg
  padding lilMrg
  background menuBg
  user-select none
  cursor default

input
button:not(:last-child)
  margin-right lilMrg
  
[type='range']
  width 10em
  cursor grab
  
[type='number']
  max-width 4em
  background menuBg
  border none
  outline none

button
  background menuBg
  border none
  outline none
  cursor pointer
  
button:hover
  background highlightBg
  
nav:last-child  
nav:first-child button:last-child
nav:last-child button:last-child
  float right
  
#toolbar.collapsed nav:not(:first-child)
  height 0
  overflow hidden
  border 0
  margin 0
  padding 0
            
          
!
            
              //imported THREE from https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js
//imported THREE.OrbitControls from https://threejs.org/examples/js/controls/OrbitControls.js

//prevent autorun log spam
console.clear()

///shortcut junk
const pi = Math.PI,
      pi2 = pi * 2,
      hpi = pi / 2,
      rad = pi / 180,
      pow = Math.pow,
      sqrt = Math.sqrt,
      sin = Math.sin,
      cos = Math.cos,
      abs = Math.abs,
      angleBetween = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1),
      eps = 1E-6,
      
      flatGeom = (type, ...args) => {
        let geom = new THREE[type[0].toUpperCase() + type.slice(1, Infinity) +'Geometry'](...args)

        geom.computeFlatVertexNormals()

        return geom
      }


///Basic scene
const renderer = new THREE.WebGLRenderer(),
      scene = new THREE.Scene(),
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000),
      controls = new THREE.OrbitControls(camera, renderer.domElement),
      
      //aLight = new THREE.AmbientLight(0x222222),
      //dLight = new THREE.DirectionalLight(0xffffff, 0.5),
      
      gridXZ = new THREE.GridHelper(100, 10)
    
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
document.body.appendChild(renderer.domElement)

camera.position.y = 20;
camera.position.z = 20;
camera.position.x = 20;
camera.lookAt(new THREE.Vector3(0, 0, 0))

//dLight.position.set(50, 20, 0)
//dLight.castShadow = true
//dLight.shadow.mapSize.width = dLight.shadow.mapSize.height = 64

//scene.add(aLight)
//scene.add(dLight)
scene.add(gridXZ)


///kepler orbits
class Orbital {
  constructor (a = 10, e = 0, p = 0, i = 0, l = 0, m = 1, t = 0) {
    this.baseMatrix = new THREE.Matrix4
    this.offsetMatrix = new THREE.Matrix4
    
    this.matrix = new THREE.Matrix4
    this.euler = new THREE.Euler(0, 0, 0,'XZY')
    this.vector = new THREE.Vector3
    
    this.baseVector = new THREE.Vector2
    
    this.majorRadius = a
    this.eccentricity = e
    this.periapsis = p
    this.inclination = i
    this.ascendingLongitude = l
    
    this.parentMass = m
    this.phase = t
  }
  
  get minorRadius () {
    return this.majorRadius * sqrt(1 - pow(this.eccentricity, 2))
  }
  
  get focusOffset () {
    return this.majorRadius * this.eccentricity
  }
  
  get period () {
    return pi2 * sqrt(pow(this.majorRadius, 3) / pow(this.parentMass, 2))
  }

  meanAnomaly (time) {
    let period = this.period
    
    return (pi2 * time + this.phase * period * pi2) / period
  }
  
  eccentricAnomaly (meanAnomaly) {
    let E = meanAnomaly, E_next = 0, loops = 0
    
    while ( loops++ < 10 ) {
      E_next = E + (meanAnomaly - (E - this.eccentricity *  sin(E))) / (1 - this.eccentricity * cos(E))
      
      if (abs(E_next - E) < eps) break
      
      E = E_next
    }
    
    return E
  }
  
  trueAnomaly (eccentricAnomaly) {
    let eccSqr = pow(this.eccentricity, 2),
        cosAnom = cos(eccentricAnomaly),
        denom = 1 - this.eccentricity * cosAnom,
        
        cosF = (cosAnom - this.eccentricity) / denom,
        sinF = (sqrt(1 - eccSqr) * sin(eccentricAnomaly)) / denom,
        len = this.majorRadius * (1 - eccSqr) / (1 + this.eccentricity * cosF)
    
    this.baseVector.set(len * cosF, len * sinF)
    
    this.offsetMatrix.makeRotationFromEuler(this.euler.set(pi,-angleBetween(this.baseVector.x, this.baseVector.y, -this.focusOffset, 0),pi))
    
    this.baseMatrix.makeTranslation(this.baseVector.x, 0, this.baseVector.y).multiply(this.offsetMatrix)
    
    this.offsetMatrix.makeRotationFromEuler(this.euler.set(this.inclination * pi, this.periapsis * pi, this.ascendingLongitude * pi))
    
    this.matrix.identity().multiply(this.offsetMatrix).multiply(this.baseMatrix)
    
    return this.vector.set(0,0,0).applyMatrix4(this.matrix)
  }
  
  solveForVector (time = 0) {
    return this.trueAnomaly(this.eccentricAnomaly(this.meanAnomaly(time)))
  }
  
  solveForMatrix (time = 0) {
    this.trueAnomaly(this.eccentricAnomaly(this.meanAnomaly(time)))
    
    return this.matrix
  }
}

class OrbitalPath {
  constructor (orbital) {
    this.orbital = orbital
    this.mesh = new THREE.Line(new THREE.Geometry, new THREE.LineBasicMaterial({ color : 0xff0000 }))
  }
  
  update () {
    if(!this.orbital) return;
    
    let geom = this.mesh.geometry,
        width = this.orbital.majorRadius,
        height = this.orbital.minorRadius,
        offset = this.orbital.focusOffset
    
    
    for(let i = 0, j = 0, step = pi2 / 32, max = pi2 + step; i <= max; i += step, j++) geom.vertices[j] = new THREE.Vector3(Math.cos(i) * width - offset, 0, Math.sin(i) * height).applyMatrix4(this.orbital.offsetMatrix)
    
    this.mesh.geometry.verticesNeedUpdate = true
  }
}



///init
let sun = new THREE.Mesh(flatGeom('icosahedron', 1, 1), new THREE.MeshStandardMaterial({ emissive: 0xFFFF60, emissiveIntensity: 1.2 })),
    pLight = new THREE.PointLight(0xffffDE, 1, 60),
    planet1 = new THREE.Mesh(flatGeom('icosahedron', .5, 1), new THREE.MeshStandardMaterial({ color: 0xFFFFFF })),
    orbiter = new Orbital,
    path = new OrbitalPath(orbiter),
    
    update = time => {
      orbiter.solveForMatrix(time).decompose(planet1.position, planet1.rotation, planet1.scale)
      path.update()
    }




sun.position.y = 5




orbiter.majorRadius = 7
orbiter.parentMass = 3
orbiter.eccentricity = 0.7
orbiter.periapsis = -0.5
orbiter.inclination = 0.25
orbiter.ascendingLongitude = 0.15
orbiter.phase = .5

update()
sun.add(pLight)
sun.add(path.mesh)
planet1.add(new THREE.AxisHelper())
sun.add(planet1)
scene.add(sun)



///loop
let now = 0,
    lastFrame = Date.now(),
    paused = true,
    opening = true,
    camRad = 50,
    camHeight = 4,
    camPhase = pi,
    destination = new THREE.Vector3(-15, 10, 15),
    alpha = 1,
    alpha2 = 1,
    tgt = new THREE.Vector3,
    pos = new THREE.Vector3,
    demo = false

;(function animate () {
  requestAnimationFrame(animate)
  let time = Date.now(),
      delta = time - lastFrame
  
  if(!opening) {
    if(demo) camera.lookAt(planet1.position)
    
    if(!paused) update(now += delta / 100)
  } else {
    camPhase += delta / 1000
    
    if(camPhase >= pi2) {
      if(alpha > 0 || alpha2 > 0) {
        alpha = Math.max(0, alpha - delta / 2000)
        alpha2 = Math.max(0, alpha2 -delta / 1000)
      } else opening = paused = false, demo = true
    }
    
    pos.x = cos(camPhase) * camRad
    pos.y = cos(camPhase + pi) * camHeight + camHeight + 1
    pos.z = sin(camPhase) * camRad
    
    camera.position.lerpVectors(destination, pos, alpha)
    camera.lookAt(tgt.lerpVectors(planet1.position, sun.position, alpha))
  }
  
  renderer.render(scene, camera)
  
  lastFrame = time
})()



///Interface
window.addEventListener('resize', (function size () {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  
  renderer.setSize(window.innerWidth, window.innerHeight)
  
  return size
})())



let toolbar = document.body.appendChild(document.createElement('section'))

toolbar.id = 'toolbar'



let buttons = toolbar.appendChild(document.createElement('nav')),
    pButton = buttons.appendChild(document.createElement('button')),
    sButton = buttons.appendChild(document.createElement('button')),
    dButton = buttons.appendChild(document.createElement('button')),
    
    toolbarOut = false,
    toolbarSwitch = buttons.appendChild(document.createElement('button'))

pButton.innerHTML = 'pause'
sButton.innerHTML = 'stop'
dButton.innerHTML = 'track'
toolbarSwitch.innerHTML = 'options'

toolbar.classList.add('collapsed')

pButton.addEventListener('click', _ => {
  paused = !paused
  
  if(paused) pButton.innerHTML = 'play'
  else pButton.innerHTML = 'pause'
})

sButton.addEventListener('click', _ => {
  paused = true
  update(now = 0)
  pButton.innerHTML = 'play'
})

dButton.addEventListener('click', _ => demo = !demo)

toolbarSwitch.addEventListener('click', _ => {
  toolbarOut = !toolbarOut
  
  if(toolbarOut) {
    toolbar.classList.remove('collapsed')
    toolbarSwitch.innerHTML = 'collapse'
  } else {
    toolbar.classList.add('collapsed')
    toolbarSwitch.innerHTML = 'options'
  }
})



let settings = {
      majorRadius: [1, 30],
      eccentricity: [0, .95],
    
    },
    defaults = {}

settings.phase = settings.ascendingLongitude = settings.inclination = settings.periapsis = [-1, 1]
settings.parentMass = [.1, 30]

for(let s in settings) {
  let originalValue = orbiter[s],
      
      elem = toolbar.appendChild(document.createElement('nav')),
      title = elem.appendChild(document.createElement('h2')),
      range = elem.appendChild(document.createElement('input')),
      number = elem.appendChild(document.createElement('input')),
      reset = elem.appendChild(document.createElement('button'))
  
  title.innerHTML = s
  
  range.type = 'range'
  number.type = 'number'
  range.min = number.min = settings[s][0]
  range.max = number.max = settings[s][1]
  range.step = number.step = .01
  range.value = number.value = orbiter[s]
  
  range.addEventListener('input', () => {
    number.value = orbiter[s] = parseFloat(range.value)
    update(now)
  })
  
  number.addEventListener('input', () => {
    range.value = orbiter[s] = parseFloat(number.value)
    update(now)
  })
  
  reset.innerHTML = 'R'
  defaults[s] = _ => {
    range.value = number.value = orbiter[s] = originalValue
  }
  
  reset.addEventListener('click', () => {
    defaults[s]()
    update(now)
  })
}



let reset = toolbar.appendChild(document.createElement('nav')).appendChild(document.createElement('button'))

reset.innerHTML = 'reset all'

reset.addEventListener('click', _ => {
  for(let s in defaults) defaults[s]()
  update(now)
})
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console