b3d-star

Procedural star mesh using a subdivided cube projected onto a sphere. Surface detail from 3D Perlin noise creates sunspot/granulation patterns via vertex colors. Spectral class controls the star's color temperature.

Optional corona (glow shell) surrounds the star.

Demo

import { b3d, b3dSun, b3dSkybox, b3dLight, b3dStar, b3dSphere, label3d, slider3d, toggle3d, select3d } from 'tosijs-3d'
import { tosi, elements } from 'tosijs'
const { div, p } = elements

const { demo } = tosi({
  demo: {
    spectralClass: 'G',
    radius: 20,
    surfaceDetail: 0.5,
    coronaSize: 0.3,
    glowIntensity: 1.1,
    rotationSpeed: 0.02,
    wireframe: false,
    pointLight: true,
    lightIntensity: 1.0,
  },
})

const star = b3dStar({
  seed: 42,
  radius: demo.radius,
  subdivisions: 32,
  spectralClass: demo.spectralClass,
  surfaceDetail: demo.surfaceDetail,
  coronaSize: demo.coronaSize,
  glowIntensity: demo.glowIntensity,
  rotationSpeed: demo.rotationSpeed,
  wireframe: demo.wireframe,
  pointLight: demo.pointLight,
  lightIntensity: demo.lightIntensity,
})

const scene = b3d(
  {
    frameRate: 60,
    clearColor: '#000000',
    scenePanel: () => [
      label3d({ text: 'Star' }),
      select3d({ label: 'spectral class', value: demo.spectralClass, options: ['O', 'B', 'A', 'F', 'G', 'K', 'M'] }),
      slider3d({ label: 'radius', value: demo.radius, min: 5, max: 50, step: 1 }),
      slider3d({ label: 'surface detail', value: demo.surfaceDetail, min: 0, max: 1, step: 0.05 }),
      slider3d({ label: 'corona size', value: demo.coronaSize, min: 0, max: 0.5, step: 0.01 }),
      slider3d({ label: 'glow', value: demo.glowIntensity, min: 0, max: 3, step: 0.1 }),
      slider3d({ label: 'rotation', value: demo.rotationSpeed, min: 0, max: 0.2, step: 0.005 }),
      slider3d({ label: 'light intensity', value: demo.lightIntensity, min: 0, max: 5, step: 0.1 }),
      toggle3d({ label: 'wireframe', value: demo.wireframe }),
      toggle3d({ label: 'point light', value: demo.pointLight }),
    ],
    sceneCreated(el, BABYLON) {
      const camera = new BABYLON.ArcRotateCamera(
        'orbit-cam',
        -Math.PI / 2,
        Math.PI / 3,
        80,
        BABYLON.Vector3.Zero(),
        el.scene
      )
      camera.lowerRadiusLimit = 30
      camera.upperRadiusLimit = 300
      camera.minZ = 0.5
      camera.maxZ = 2000
      camera.attachControl(el.querySelector('canvas'), true)
      el.setActiveCamera(camera)
    },
  },
  b3dLight({ intensity: 0.1 }),
  star,
  b3dSphere({ radius: 5, x: 40, y: 0, z: 0, color: '#888888' }),
  b3dSphere({ radius: 5, x: -40, y: 0, z: 0, color: '#888888' }),
)

preview.append(
  scene,
  div({ class: 'debug-panel' }, p('Scroll to zoom, drag to orbit')),
)

for (const key of ['radius', 'surfaceDetail']) {
  demo[key].observe(() => {
    star.regenerate()
  })
}
for (const key of ['spectralClass', 'coronaSize', 'glowIntensity', 'wireframe', 'rotationSpeed', 'pointLight', 'lightIntensity']) {
  demo[key].observe(() => {
    star.updateOptions()
  })
}
tosi-b3d {
  width: 100%;
  height: 100%;
}
.debug-panel {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(0,0,0,0.6);
  color: white;
  padding: 8px 12px;
  border-radius: 6px;
  font: 12px monospace;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

Attributes

Attribute Default Description
seed 12345 Noise seed for surface features
radius 20 Star radius
subdivisions 32 Grid subdivisions per cube face
spectralClass 'G' Spectral class: O, B, A, F, G, K, M
surfaceDetail 0.5 Surface noise intensity (0=uniform, 1=strong spots)
coronaSize 0.15 Corona thickness as fraction of radius (0=none)
rotationSpeed 0.02 Auto-rotation (radians/sec)
wireframe false Debug wireframe
glowIntensity 2.0 Emissive brightness multiplier
pointLight false Create a point light at star center
lightIntensity 1.0 Point light intensity
lightRange 500 Point light range