Flickering Torch

Noise-based point light flicker simulating a handheld torch.

What this code does

- Procedural Flicker: combines sine waves with pseudorandom noise for realistic torch flicker.
- PRNG Function: seeded random number generator ensures consistent but unpredictable behavior.
- Dynamic Intensity: point light intensity varies based on flicker algorithm.
- Visual Flame: small sphere scales with light intensity to simulate flame size.
- Interactive Controls: adjust flicker intensity and speed in real-time.
- Atmospheric Lighting: warm orange light creates cozy torch ambiance.

JavaScript (plain)

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

function prng (seed) {
  let s = seed >>> 0
  return () => {
    s ^= s << 13
    s ^= s >>> 17
    s ^= s << 5
    return (s >>> 0) / 0xffffffff
  }
}

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(70, width / height, 0.1, 100)
camera.position.set(0, 1.5, 3.5)

const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)

document.querySelector('#app').appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(0, 1, 0)
controls.update()

const plane = new THREE.Mesh(
  new THREE.PlaneGeometry(10, 10),
  new THREE.MeshStandardMaterial({ color: 0x0e0e0e })
)
plane.rotation.x = -Math.PI / 2
plane.position.y = 0
scene.add(plane)

const wall = new THREE.Mesh(
  new THREE.PlaneGeometry(10, 4),
  new THREE.MeshStandardMaterial({ color: 0x080808 })
)
wall.position.set(0, 2, -3)
scene.add(wall)

const torch = new THREE.PointLight(0xffaa55, 2, 10, 2)
torch.position.set(0, 1.5, 0)
scene.add(torch)

const flame = new THREE.Mesh(
  new THREE.SphereGeometry(0.05, 8, 8),
  new THREE.MeshBasicMaterial({ color: 0xffddaa })
)
flame.position.copy(torch.position)
scene.add(flame)

const rng = prng(1337)
let time = 0

function animate(t) {
  requestAnimationFrame(animate)
  time += (t || 16) / 1000
  const noise = (rng() - 0.5) * 2
  const n2 = (rng() - 0.5) * 2
  const flickerIntensity = 0.6
  const speed = 6.0
  const base = 1.8
  const flick = base + flickerIntensity * (Math.sin(time * speed) * 0.5 + 0.5 * noise + 0.25 * n2)
  torch.intensity = Math.max(0.2, flick)
  flame.scale.setScalar(0.8 + (torch.intensity - base) * 0.2)
  controls.update()
  renderer.render(scene, camera)
}
animate()