Note
Go to the end to download the full example code.
Quickstart
A pulse2percept simulation has three parts:
an implant: the device and its electrodes,
a stimulus: what is delivered to the electrodes, and
a model: how stimulation becomes a predicted percept.
Retinal and cortical simulations use the same pattern.
A single-electrode phosphene
In epiretinal implants like Argus II, phosphenes often appear elongated along
retinal nerve fiber bundles [Beyeler2019].
The [Granley2021] model captures this effect and predicts phosphene shape,
brightness, and size from pulse amplitude (given as a multiple of perceptual
threshold, xTh), frequency (Hz), and pulse duration (ms):
import matplotlib.pyplot as plt
import pulse2percept as p2p
from pulse2percept.units import Hz, um, mm, ms, uA, xTh, dva
retinal_implant = p2p.implants.retina.ArgusII()
retinal_model = p2p.models.retina.BiphasicAxonMapModel(
retinal_implant,
rho=300 * um, # microns
lam=500 * um, # microns
)
stim = {
'A5': p2p.stimuli.BiphasicPulseTrain(
freq=20 * Hz, # Hertz (pulses/s)
amp=2 * xTh, # multiples of threshold
phase_dur=0.45 * ms, # milliseconds
)
}
percept = retinal_model.predict_percept(stim)
percept.plot()
plt.title('Argus II: one stimulated electrode')
plt.show()

The biphasic axon map model [Granley2021] is based on human behavioral data collected across multiple retinal prosthesis studies. Its two spatial parameters, rho and lam, control phosphene spread perpendicular and parallel to the retinal nerve fiber bundles, respectively. These parameters vary across patients, so the values above are illustrative rather than universal.
An image through a photovoltaic implant
PRIMA is a subretinal photovoltaic implant driven by pulsed near-infrared
light. PRIMAPivotal includes a
PRIMAEncoder that converts image intensity
into the pulse durations delivered by the projector.
Below we pair the implant with
Ho2018Model, which models the
transient retinal network response reported by [Ho2018].
prima = p2p.implants.retina.PRIMAPivotal()
prima_model = p2p.models.retina.Ho2018Model(
prima,
xrange=(-6 * dva, 6 * dva), # degrees of visual angle
yrange=(-6 * dva, 6 * dva),
step=0.1 * dva,
)
image = p2p.stimuli.samples.ucsb_surf(resize=(180, 320))
percept = prima_model.predict_percept(image, t_percept=50 * ms)
The model is based on degenerated rat retina and should not be interpreted as a validated human model of PRIMA perception.
Adding residual vision and gaze
A Scene places an image or video in the
visual field. It can also include a Scotoma
describing where native vision has been lost.
Here a video recorded on the UCSB campus spans 40 degrees of visual angle (dva), with a central scotoma representing vision loss around the implanted region.
video = p2p.stimuli.samples.ucsb_pedestrians(resize=(173, 320))
scotoma = p2p.vision.Scotoma.circle(5 * dva)
scene = p2p.vision.Scene(
video,
fov=40 * dva,
scotoma=scotoma,
scotoma_fill=0,
aperture='round',
)
gaze = p2p.vision.Gaze(
[(0, 0), (-15.5, -6), (12, -6)] * dva,
time=[0, 635, 1370] * ms
)
percept = prima_model.predict_percept(scene, gaze=gaze)
scene.play(
percept=percept,
gaze=gaze,
vmax=percept.data.max(),
)
A form traced through visual cortex
Cortical phosphenes do not necessarily combine like pixels on a screen. In human experiments with the Orion visual cortical prosthesis, simultaneous stimulation of several electrodes often produced merged phosphenes rather than a recognizable shape. Beauchamp et al. [Beauchamp2020] instead stimulated electrodes sequentially, tracing letter-like forms through the retinotopic map.
Here we borrow that stimulation strategy, but visualize the resulting
spatiotemporal percept with the independently developed
DynaphosModel
[vanderGrinten2023]. An Orion array is mapped from cortex into the visual
field, and a sequence of electrodes traces a letter over time:
NeuroPort array on V1 and stimulate every electrode with the same pulse train.
TODO: composed view, orion lighting up on the left, using TraceEncoder
to draw a Z on the right.
polimeni_map = p2p.topography.cortex.Polimeni2006Map(regions=['v1'])
orion = p2p.implants.cortex.Orion()
dynaphos = p2p.models.cortex.DynaphosModel(
orion,
visual_field_map = polimeni_map,
implant_position=(20, -5) * mm,
xrange=(-3 * dva, -1 * dva),
yrange=(0, 2 * dva),
step=0.01 * dva,
)
train = p2p.stimuli.BiphasicPulseTrain(
freq=20 * Hz,
amp=100 * uA,
phase_dur=0.45 * ms,
)
stim = {electrode: train for electrode in cortical_implant.electrode_names}
percept = cortical_model.predict_percept(stim)
percept.plot()
plt.title('NeuroPort Array: all 96 electrodes')
plt.show()
Traceback (most recent call last):
File "C:\Users\thela\Documents\GitHub\pulse2percept\examples\plot_quickstart.py", line 157, in <module>
stim = {electrode: train for electrode in cortical_implant.electrode_names}
^^^^^^^^^^^^^^^^
NameError: name 'cortical_implant' is not defined
This is therefore an illustrative simulation, not a reproduction of the Beauchamp participant: electrode locations, thresholds, and phosphene dynamics vary across people.
Changing the simulation
Implant: another retinal or cortical device, the implanted eye or hemisphere, measured electrode thresholds, or a different encoder.
Stimulus: electrode, amplitude, frequency, phase duration, or an image or video that the implant encodes into pulses.
Model: chosen for the implant and the question. Scoreboard models produce round phosphenes; axon map models produce elongated retinal phosphenes.
Next steps
The Core Concepts pages cover each part in order, starting with Implants. The key examples show complete workflows with images, video, and encoders.
Total running time of the script: (0 minutes 10.088 seconds)