Mohamed Yassine Hemissi

Student • Dreamer • Human

HomeProjectsBlog

© 2026 Mohamed Yassine Hemissi. All rights reserved.

Back to home

Lava Lamp Model

April 1, 2026·8 min read·By Mohamed Yassine Hemissi

Model

Overview

This project does not solve full fluid dynamics. The lava motion is modeled as a small set of blobs moving inside a bounded lamp volume under a simplified, physics-inspired update rule.

Each blob carries:

  • position: xi(t)∈R3x_i(t) \in \mathbb{R}^3xi​(t)∈R3
  • velocity: vi(t)∈R3v_i(t) \in \mathbb{R}^3vi​(t)∈R3
  • temperature: Ti(t)∈RT_i(t) \in \mathbb{R}Ti​(t)∈R
  • mass: mi>0m_i > 0mi​>0

The simulator then:

  1. updates blob temperatures
  2. computes forces on each blob
  3. integrates velocity and position with a fixed timestep
  4. rebuilds a scalar field for rendering

The main implementation files are:

  • PhysicsSimulator.ts
  • physicsSimulatorTimeStep.ts
  • LavaLampRenderer.ts
  • lampRenderer.ts

State

For blob i, the state is:

si(t)=(xi(t),vi(t),Ti(t),mi)s_i(t) = (x_i(t), v_i(t), T_i(t), m_i)si​(t)=(xi​(t),vi​(t),Ti​(t),mi​)

The mean temperature is:

Tˉ(t)=1N∑i=1NTi(t)\bar{T}(t) = \frac{1}{N} \sum_{i=1}^{N} T_i(t)Tˉ(t)=N1​i=1∑N​Ti​(t)

The normalized height of a blob inside the lamp bounds is:

hi(t)=clamp(xi,y(t)−ymin⁡ymax⁡−ymin⁡,0,1)h_i(t) = \mathrm{clamp}\left( \frac{x_{i,y}(t)-y_{\min}}{y_{\max}-y_{\min}}, 0, 1 \right)hi​(t)=clamp(ymax​−ymin​xi,y​(t)−ymin​​,0,1)

Temperature Evolution

The temperature model is:

dTidt=Hglobal+Hbottom(hi)+Hstochastic(i,t)−Ccool(Ti,hi)−Ddiff(Ti,Tˉ)\frac{dT_i}{dt} = H_{\mathrm{global}} + H_{\mathrm{bottom}}(h_i) + H_{\mathrm{stochastic}}(i,t) - C_{\mathrm{cool}}(T_i,h_i) - D_{\mathrm{diff}}(T_i,\bar{T})dtdTi​​=Hglobal​+Hbottom​(hi​)+Hstochastic​(i,t)−Ccool​(Ti​,hi​)−Ddiff​(Ti​,Tˉ)

with:

Global Heating

Hglobal=GH_{\mathrm{global}} = GHglobal​=G

where G = globalHeating.

Bottom Heating Bias

Hbottom(hi)=(1−hi)BH_{\mathrm{bottom}}(h_i) = (1-h_i)BHbottom​(hi​)=(1−hi​)B

where B = bottomHeatingBias.

Top Cooling Bias

Ctop(hi)=max⁡(hi−hcool,0) CtC_{\mathrm{top}}(h_i) = \max(h_i - h_{\mathrm{cool}}, 0)\,C_tCtop​(hi​)=max(hi​−hcool​,0)Ct​

where:

  • h_cool = topCoolingThreshold
  • C_t = topCoolingBias

Cooling Toward Ambient

Ccool(Ti,hi)=(C+Ctop(hi))(Ti−Ta)C_{\mathrm{cool}}(T_i,h_i) = (C + C_{\mathrm{top}}(h_i))(T_i - T_a)Ccool​(Ti​,hi​)=(C+Ctop​(hi​))(Ti​−Ta​)

where:

  • C = coolingRate
  • T_a = ambientTemperature

Diffusion Toward Mean Temperature

Ddiff(Ti,Tˉ)=D(Ti−Tˉ)D_{\mathrm{diff}}(T_i,\bar{T}) = D(T_i - \bar{T})Ddiff​(Ti​,Tˉ)=D(Ti​−Tˉ)

where D = diffusionRate.

Stochastic Thermal Forcing

The stochastic term is deterministic per blob id but time-varying:

Hstochastic(i,t)=A S(i,t)H_{\mathrm{stochastic}}(i,t) = A\,S(i,t)Hstochastic​(i,t)=AS(i,t)

where:

  • A = stochasticAmplitude
  • S(i,t) is a weighted trigonometric mixture

The code uses:

S(i,t)=0.88 P(i,t)+0.24 sin⁡(0.21ωt+0.63ϕi)+0.17 sin⁡(0.47ωt−0.31ϕi)+0.08 sin⁡(1.16ωt+1.37ϕi)S(i,t) = 0.88\,P(i,t) + 0.24\,\sin(0.21\omega t + 0.63\phi_i) + 0.17\,\sin(0.47\omega t - 0.31\phi_i) + 0.08\,\sin(1.16\omega t + 1.37\phi_i)S(i,t)=0.88P(i,t)+0.24sin(0.21ωt+0.63ϕi​)+0.17sin(0.47ωt−0.31ϕi​)+0.08sin(1.16ωt+1.37ϕi​)

where:

  • omega = stochasticFrequency
  • phi_i is a stable phase derived from the blob id
  • P(i,t) is a plateau-shaped version of the primary sine wave

The explicit timestep update is:

Tin+1=Tin+Δt(G+(1−hi)B+A S(i,tn)−(C+Ctop(hi))(Tin−Ta)−D(Tin−Tˉn))T_i^{n+1} = T_i^n + \Delta t \left( G + (1-h_i)B + A\,S(i,t_n) - (C + C_{\mathrm{top}}(h_i))(T_i^n - T_a) - D(T_i^n - \bar{T}^n) \right)Tin+1​=Tin​+Δt(G+(1−hi​)B+AS(i,tn​)−(C+Ctop​(hi​))(Tin​−Ta​)−D(Tin​−Tˉn))

This is implemented in updateBlobTemperature(...) in physicsSimulatorTimeStep.ts.

Forces

The total force on a blob is:

Fi=Fbuoyancy,i+Fgravity,i+Fdrag,i+Fboundary,iF_i = F_{\mathrm{buoyancy},i} + F_{\mathrm{gravity},i} + F_{\mathrm{drag},i} + F_{\mathrm{boundary},i}Fi​=Fbuoyancy,i​+Fgravity,i​+Fdrag,i​+Fboundary,i​

Buoyancy

Buoyancy is driven by temperature difference from ambient:

Fbuoyancy,i=[0miβ(Ti−Ta)g0]F_{\mathrm{buoyancy},i} = \begin{bmatrix} 0 \\ m_i \beta (T_i - T_a) g \\ 0 \end{bmatrix}Fbuoyancy,i​=​0mi​β(Ti​−Ta​)g0​​

where:

  • beta = buoyancyBeta
  • g = gravity

Gravity

Fgravity,i=[0−mig0]F_{\mathrm{gravity},i} = \begin{bmatrix} 0 \\ -m_i g \\ 0 \end{bmatrix}Fgravity,i​=​0−mi​g0​​

Drag

Fdrag,i=−cdviF_{\mathrm{drag},i} = -c_d v_iFdrag,i​=−cd​vi​

where c_d = dragCoefficient.

Boundary Force

Near the lamp wall, the model applies a spring-like restoring force plus damping:

Fboundary,i≈k p−cb v⊥F_{\mathrm{boundary},i} \approx k\,p - c_b\,v_{\perp}Fboundary,i​≈kp−cb​v⊥​

where:

  • k = stiffness
  • c_b = damping
  • p is penetration into the boundary margin
  • v_perp is the outward velocity component

This is applied axis-by-axis in computeBoundaryForce(...).

Motion Integration

Acceleration:

ai=Fimia_i = \frac{F_i}{m_i}ai​=mi​Fi​​

Velocity update:

vin+1=vin+Δt ainv_i^{n+1} = v_i^n + \Delta t\,a_i^nvin+1​=vin​+Δtain​

Position update:

xin+1=xin+Δt vin+1x_i^{n+1} = x_i^n + \Delta t\,v_i^{n+1}xin+1​=xin​+Δtvin+1​

Then the blob is clamped back into the valid bounds:

xin+1←ΠΩ(xin+1)x_i^{n+1} \leftarrow \Pi_{\Omega}(x_i^{n+1})xin+1​←ΠΩ​(xin+1​)

and outward velocity is zeroed on collision.

This logic is implemented by advanceBlobs(...) in physicsSimulatorTimeStep.ts.

Time Stepping

The simulator uses a fixed timestep:

Δt=fixedDeltaTimeMs1000\Delta t = \frac{\texttt{fixedDeltaTimeMs}}{1000}Δt=1000fixedDeltaTimeMs​

Real frame time is accumulated, and multiple internal substeps may run before rendering. This is handled in step(...) in PhysicsSimulator.ts.

Scalar Field for Rendering

The blobs are not the final visible lava surface. Instead they generate a scalar field:

F(x)=∑i=1Nsi ϕ(∥x−xi∥;ri)F(x) = \sum_{i=1}^{N} s_i\,\phi(\|x-x_i\|; r_i)F(x)=i=1∑N​si​ϕ(∥x−xi​∥;ri​)

where:

  • s_i is blob strength
  • r_i is blob influence radius
  • phi is the radial contribution kernel
  • some blobs are free interior blobs and some are anchored cap blobs that keep small persistent masses at the top and bottom extremes

That field is rebuilt in rebuildFieldSnapshot() and later rendered as a liquid surface.

Runtime Controls

The exposed controls do not change the structure of the model. They rescale parts of it:

  • temperature scales global heating, bottom heating, top cooling, and stochastic amplitude
  • buoyancy scales buoyancy and slightly rebalances gravity
  • damping scales drag and boundary damping
  • diffusion scales thermal diffusion and field softness

These mappings are implemented in setParameter(...) in PhysicsSimulator.ts.

Interpretation

The current simulation is best described as:

  • a bounded particle system
  • with temperature-driven buoyancy
  • gravity
  • linear drag
  • soft wall response
  • deterministic stochastic thermal forcing

It is intentionally a simplified, controllable demo model rather than a full physical fluid solver.