Setting up a Potential

Let's refresh our memory with a look at the full Schrödinger equation: i t Ψ x t = - 2 2 m 2 x 2 Ψ x t + V x Ψ x t

Now that we have a particle in our simulation, we make things really interesting by giving it something to interact with. In quantum mechanics, this interaction is determined by the potential term, V x , in the Schrödinger equation. Indeed it is in the interactions with changing potentials that quantum mechanics displays behavior that differs strongly from classical physics.

Being able to experiment with, and visualize, these interactions and behaviors is a powerful tool for developing a strong intuitive understanding of quantum behaviors. The wave function's interaction with a varying potential also affords us another opportunity to examine the code's correctness and stability.

In order to visualize the potential and its effect on the wave function, we need to expand the SchrodingerResults to include the potential, the energy of the incoming particle, and also the real and imaginary components of the wave function. To differentiate it from the previous version we'll call it SchrodingerRenderer.

The big change for the SchrodingerRenderer is that the components of the wave function can be negative. Instead of ranging from 0 to PsiMax, the y axis now ranges from -PsiMax to PsiMax. At texture coordinate t = 0, psi = -PsiMax, and at t = 1.0, psi = PsiMax. Of course, psi = 0 at t = 0.5. We get exactly this scale if we take the psi that pixel corresponds to as psi = 2*PsiMax*t - PsiMax

We make two additional modifications to the rendering code. We use a combination of smoothsteps to soften the lines a bit, and when the values have changed by more than one vertical pixel we fill in the curve with a short vertical line. We utilize the same pixelColor method for each of the | Ψ x t | 2 , Re ( Ψ x t ) , Im ( Ψ x t ) , V x and the initial particle energy E 0 curves.

  vec4 pixelColor(vec4 color, float scale, float t, float halfDeltaY, float value, float previousValue)
    vec4  pixel;
    float scale2;

    scale2 = 2.0*scale;

    pixel = color*(smoothstep(scale2*(t-2.0*halfDeltaY)-scale, scale2*(t-halfDeltaY)-scale, value)
                   - smoothstep(scale2*(t+halfDeltaY)-scale, scale2*(t+2.0*halfDeltaY)-scale, value))
    // Fill in discontinuities in the function
          + color*(step(previousValue+scale2*halfDeltaY, value)
                   *(step(previousValue, scale2*t-scale) - step(value, scale2*t-scale)))
          + color*(step(value, previousValue+scale2*halfDeltaY)
                   *(step(value, scale2*t-scale) - step(previousValue, scale2*t-scale)));

    return pixel;

This code produces a readily understandable graph of the wave function as it propagates and interacts with the potential step.

Ψ x t with E 0 and V x .

This example shows our first unambiguously quantum behavior. At the potential step, the wave functions splits in two, and is half reflected, and half transmitted. This represents roughly equal probabilities of detecting the particle as being reflected or as being transmitted. It does not correspond to the particle splitting into two.

We also did something a bit different in setting up the potential. Rather than write a new shader to populate the potential array, we utilize a JavaScript function. This makes it much easier for later users and content authors to modify the potential to their exact needs.

   * Populate the potential array
  function stepPotential(potential)
    var nelements;

    // Remember, RGBA components for each texel
    nelements = potential.length/4;

    for(var i=0; i<nelements; ++i) {
      if (i<nelements/3) {
        potential[4*i] = 0.0;
      else if (i<nelements/3+10) {
        potential[4*i] = 100.0;
      else {
        potential[4*i] = 0.0;
    return potential;
Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.