text: function stpdep() {
  // Two neurons is a little app that lets people play with different synapse types
  // For Ampa: just simple processes
  // For Nmda: timing is really important. A range slider showing the diff in spike times between 
  // For GABAa: show how it 
  // for GABAb: show how it 
  
  ////////////////////////////////////////////////////////////////////////////////
  // define the network
  ////////////////////////////////////////////////////////////////////////////////
  const stpdep = document.getElementById("stpdep");
  const width  = stpdep.offsetWidth;
  stpdep.style.height = `${stpdep.offsetWidth*4/16}px`;
  const height = stpdep.offsetHeight;
  // the network visualizer
  const networkSvg = document.getElementById("stpdep-svg");
  const w = 0.66*stpdep.offsetWidth;
  const h = 0.50*stpdep.offsetHeight;
  
  const vertices = [
      {id: 0, x: w/2 + 50, y: h/2},
      {id: 1, x: w/2 - 50, y: h/2}
  ];
  const edges = [
      {source: vertices[0], target: vertices[1], right: false, left: true},
  ];
  const graph = {
      vertices: vertices,
      edges   : edges
  }
  
  const simVertices = vertices.slice(0, 1);
  const simEdges = [];
  const simGraph = {
      vertices: simVertices,
      edges   : simEdges
  };
  ////////////////////////////////////////////////////////////////////////////////
  // define the neural network
  ////////////////////////////////////////////////////////////////////////////////
  const milliseconds = 100;
  const dt = 0.1;
  const time = milliseconds/dt;
  const stepsPerFrame = 5;
  function defaultNeuronData(node) {
      const data = {
          population :    1,
          external: true,
          A    :  0.02,
          B    :  0.25,
          C    :   -55,
          D    :     4,
          dt   :   dt,
          self : {
              weight: 0
          }
      }
      return data;
  }
  function defaultSynapseData(edge) {
      const data = {
          left : {
              weight           : 1,
              transmissionDelay: 1,
          },
          right : {
              weight           : 1,
              transmissionDelay: 1,
          }
      }
      return data;
  }
  simVertices.map((vertex) => vertex.data = defaultNeuronData());
  simEdges.map((edge) => edge.data = defaultSynapseData());
  
  vertices.map((vertex) => vertex.data = defaultNeuronData());
  edges.map((edge) => edge.data = defaultSynapseData());
  vertices[1].data.external = false;
  
  ////////////////////////////////////////////////////////////////////////////////
  // Sim objects
  ////////////////////////////////////////////////////////////////////////////////
  // on click of node 0: fire one of the neurons
  
  // on fire, simulate an on-click
  
  // otherwise run the code
  // create neurons
  // create weight matrix
  // set up the simulation
  // neurons
  let neurons        = graphToSimpleNeurons(simGraph.vertices, dt);
  // synapses
  let W              = graphToWeightMatrix(simGraph.vertices, simGraph.edges);
  const tAMPAdecay   = 2.0;
  const gAMPAinput   = 0.05;
  const GAMPAInput   = fill(neurons.N, gAMPAinput);
  const SAMPAInput   = fill(neurons.N, 0);
  
  const inputSynapses = [
      new AMPAExternal(GAMPAInput, SAMPAInput, tAMPAdecay, dt)
  ];
  const recurrentSynapses = [
      //new AMPARecurrent(GAMPARec, SAMPARec, tAMPAdecay, W, dt, fill(neurons.N, 1))
  ];
  const synapses = new Synapses(inputSynapses, recurrentSynapses);
  
  const spikes = [];
  for (let i = 0; i < Math.floor(0.25/dt); i++) {
      spikes.push(fill(neurons.N, 0));
  }
  
  let targetNeuron      = 0;
  let targetNeuronVs    = fill(time, -65);
  let targetNeuronInput = fill(time, 0);
  targetNeuronVs = targetNeuronVs.map((v, i) => {return {x: i*dt, y: v}})
  targetNeuronInput = targetNeuronInput.map((v, i) => {return {x: i*dt, y: v}})
  let t = targetNeuronVs[targetNeuronVs.length - 1].x;
  
  ////////////////////////////////////////////////////////////////////////////////
  // input
  ////////////////////////////////////////////////////////////////////////////////
  let stpdepF = (x, f) => f*x;
  let f = 0.5;
  let prel = 1;
  let tau = 50;
  let p0 = 1;
  
  const taulabel = document.querySelector("#stpdep-tau-label");
  const taucontroller = document.querySelector("#stpdep-tau-slider");
  taucontroller.value = tau;
  taulabel.innerText = taucontroller.value;
  taucontroller.oninput = function() {
      taulabel.innerText = taucontroller.value;
      tau = +taucontroller.value;
  }
  
  const flabel = document.querySelector("#stpdep-f-label");
  const fcontroller = document.querySelector("#stpdep-f-slider");
  fcontroller.value = f;
  flabel.innerText = fcontroller.value;
  fcontroller.oninput = function() {
      flabel.innerText = fcontroller.value;
      f = +fcontroller.value;
  }
  
  const initlabel = document.querySelector("#stpdep-init-label");
  const initcontroller = document.querySelector("#stpdep-init-slider");
  initcontroller.value = p0;
  initlabel.innerText = initcontroller.value;
  initcontroller.oninput = function() {
      initlabel.innerText = initcontroller.value;
      p0 = +initcontroller.value;
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // visualization
  ////////////////////////////////////////////////////////////////////////////////
  const N = graph.vertices.length;
  
  const externalNeurons = [false];
  
  function mouseDown(d, nodes, links, extra) {
      if (d.id === 1) {
          externalNeurons[0] = true;
          d.fired = true;
      }
  }
  
  const extraEvents = {nodeMouseDown: mouseDown};
  
  
  networkSvg.setAttribute("width", `${0.66*stpdep.offsetWidth}`);
  networkSvg.setAttribute("height", `${0.50*stpdep.offsetHeight}`);
  const dynamic  = false;
  const editable = false;
  const net = network(networkSvg, graph, dynamic, editable, extraEvents)
  const update = net.update;
  const nodes = net.nodes;
  const extra = net.extra;
  // the left plot
  const leftPlot = document.getElementById("stpdep-plot1")
  leftPlot.setAttribute("width", `${0.50*width}`);
  leftPlot.setAttribute("height", `${0.50*height}`);
  const lineGraphLeft = new LineGraph(leftPlot, [0, milliseconds], [-80, 30], "time (msec)", "membrane potential (mV)", "red");
  // the right plot
  const rightPlot = document.getElementById("stpdep-plot2")
  rightPlot.setAttribute("width", `${0.50*width}`);
  rightPlot.setAttribute("height", `${0.50*height}`);
  const lineGraphRight = new LineGraph(rightPlot, [0, milliseconds], [0, 1], "time (msec)", "P(AMPA is open)");
  // controls
  const controls = document.getElementById("stpdep-controls");
  
  // add a click me
  net.nodes.selectAll(".nodes")
           .filter((d) => d.id === 1)
           .append("text")
           .text("click me")
           .attr("text-anchor", "middle")
           .attr("transform", "translate(0, -15)")
           .attr("fill", "black");
  
  // RUN
  
  function spikeVis(neurons, graph, extra) {
      for (let i = 0; i < (neurons.N + externalNeurons.length ); i++) {
          if ((i < neurons.N && neurons.spiked[i]) || (i >= neurons.N && externalNeurons[i - neurons.N])) {
              const j = (i < neurons.N) ? i : i + neurons.N - 1;
              extra.append("circle")
                   .attr("cx", graph.vertices[j].x)
                   .attr("cy", graph.vertices[j].y)
                   .attr("r", 12)
                   .attr("stroke-width", 2)
                   .attr("stroke", "black")
                   .attr("fill", "none")
                   .transition()
                   .duration(500)
                   .ease(Math.sqrt)
                   .attr("r", 12 * 2)
                   .attr("stroke", "#f2f2f2")
                   .attr("fill", "none")
                   .remove();
          }
      }
  }
  
  function simulate(neurons, synapses, t, i) {
      if (!run && t < 50) {
          return;
      }
      if (elementInViewport(stpdep)) {
          for (let _ = 0; _ < stepsPerFrame; _++) {
              const I = prel*synapses.current(neurons.V);
              const externalSpikes = externalNeurons;
  
              const internalSpikes = spikes[(i + 1) % spikes.length];
              spikes[i % spikes.length] = neurons.spiked;
              synapses.solve(externalSpikes, internalSpikes);
              if (externalSpikes[0] === true) {
                  prel = stpdepF(prel, f);
              } else {
                  prel = prel + ((p0 - prel)/tau)*dt;
              }
              neurons.solve(I, t);
              // vis
              let v = Math.min(30, neurons.V[targetNeuron]);
              targetNeuronVs.push({x: t, y: v});
              targetNeuronInput.push({x: t, y: synapses.inputSynapses[0].S[0]*prel});
              spikeVis(neurons, graph, extra);
      
              externalNeurons[0] = false;
              t += dt;
          }
          lineGraphLeft.update(targetNeuronVs, dt*stepsPerFrame);
          lineGraphRight.update(targetNeuronInput, dt*stepsPerFrame);
  
          for (let _ = 0; _ < stepsPerFrame; _++) {
              targetNeuronVs.shift();
              targetNeuronInput.shift();
          }
      }
      // reset external
      // recurse
      window.requestAnimationFrame(() => simulate(neurons, synapses, t, i+1));
  }
  
  run = true;
  window.requestAnimationFrame(() => simulate(neurons, synapses, t, 0));
  
  }
  
  stpdep();