# Making oscilloscope images with DACs

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/078/278/medium800thumb/hacks_hameg-2dac-xy-lem-800x600.jpg?1563368132)

This project demonstrates two techniques for making images on an oscilloscope using CircuitPython and the analogue output(s) found on many Adafruit boards.

The first is an unusual technique using the oscilloscope's trigger feature and normal x-axis timebase with a&nbsp;_single_ [digital to analogue converter](https://en.wikipedia.org/wiki/Digital-to-analog_converter "Wikipedia: digital-to-analog converter") (DAC) output on a Circuit Playground Express (CPX) board. A computer running Python with the [imageio](https://pypi.org/project/imageio/ "PyPi: imageio")&nbsp;library is required to convert bitmaps into a suitable format for playback on the CPX board.

The second is the more common X-Y vector technique, using a PyGamer with its two DAC outputs.

Any SAMD21 (M0) or SAMD51 (M4) board can be used. No additional hardware is required beyond connections to the oscilloscope probes.

Thank-you to Nick for the loan of a Hameg HM203-6 oscilloscope.

## Parts
### Circuit Playground Express

[Circuit Playground Express](https://www.adafruit.com/product/3333)
 **Circuit Playground Express** is the next step towards a perfect introduction to electronics and programming. We've taken the original Circuit Playground Classic and made it even better! Not only did we pack even more sensors in, we also made it even easier to...

In Stock
[Buy Now](https://www.adafruit.com/product/3333)
[Related Guides to the Product](https://learn.adafruit.com/products/3333/guides)
![A Black woman's manicured hand holds a round microcontroller with lit up LEDs.](https://cdn-shop.adafruit.com/640x480/3333-05.jpg)

### Adafruit Feather M4 Express - Featuring ATSAMD51

[Adafruit Feather M4 Express - Featuring ATSAMD51](https://www.adafruit.com/product/3857)
It's what you've been waiting for, the Feather M4 Express featuring ATSAMD51. This Feather is fast like a swift, smart like an owl, strong like a ox-bird (it's half ox, half bird, OK?) This feather is powered by our new favorite chip, the **ATSAMD51J19** -&nbsp; with...

In Stock
[Buy Now](https://www.adafruit.com/product/3857)
[Related Guides to the Product](https://learn.adafruit.com/products/3857/guides)
![Angled shot of a Adafruit Feather M4 Express. ](https://cdn-shop.adafruit.com/640x480/3857-10.jpg)

### Adafruit PyGamer for MakeCode Arcade, CircuitPython or Arduino

[Adafruit PyGamer for MakeCode Arcade, CircuitPython or Arduino](https://www.adafruit.com/product/4242)
What&nbsp;fits in your pocket, is fully Open Source, and can run CircuitPython, MakeCode Arcade or Arduino games you write yourself? That's right, it's the **Adafruit PyGamer!** We wanted to make an entry-level gaming handheld for DIY gaming, and maybe a little...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4242)
[Related Guides to the Product](https://learn.adafruit.com/products/4242/guides)
![Angled shot of Adafruit PyGamer for MakeCode Arcade, CircuitPython or Arduino.](https://cdn-shop.adafruit.com/640x480/4242-00.jpg)

### USB cable - USB A to Micro-B

[USB cable - USB A to Micro-B](https://www.adafruit.com/product/592)
This here is your standard A to micro-B USB cable, for USB 1.1 or 2.0. Perfect for connecting a PC to your Metro, Feather, Raspberry Pi or other dev-board or microcontroller

Approximately 3 feet / 1 meter long

Out of Stock
[Buy Now](https://www.adafruit.com/product/592)
[Related Guides to the Product](https://learn.adafruit.com/products/592/guides)
![USB cable - USB A to Micro-B - 3 foot long](https://cdn-shop.adafruit.com/640x480/592-01.jpg)

# Making oscilloscope images with DACs

## Common Display Devices

## Raster Displays

The first computer monitors used single colour&nbsp;[cathode-ray tubes](https://en.wikipedia.org/wiki/Cathode-ray_tube "Wikipedia: cathode-ray tubes")&nbsp;(CRT). The beam from the electron gun in a CRT can be directed in two dimensions at the [phosphor](https://en.wikipedia.org/wiki/Phosphor "Wikipedia: phosphor") screen and often varied in intensity to control the brightness of the spot. CRT-based monitors typically use a&nbsp;[raster scan](https://en.wikipedia.org/wiki/Raster_scan "Wikipedia: raster scan")&nbsp;at a fixed rate to rapidly draw a bitmap image. If the [refresh rate](https://en.wikipedia.org/wiki/Refresh_rate "Wikipedia: refresh rate")&nbsp;(maximum frame rate) is high enough then the image is perceived as flicker-free.

Modern computer monitors use backlit LCD or LED.

## Vector Displays

The CRT can also be used in other ways, early [radar](https://en.wikipedia.org/wiki/Radar "Wikipedia: Radar")&nbsp;displayed targets (reflections) using&nbsp;a slow circular radial scan and longer persistence phosphors. The animation below shows an example from&nbsp;[Radar and Its Applications (1962)](https://archive.org/details/gov.archives.arc.892095 "US National Archives: Radar and Its Applications (1962)").

![](https://cdn-learn.adafruit.com/assets/assets/000/078/234/medium800thumb/hacks_radar-and-its-applications-ppi-radar-640x480.jpg?1563302802 CRT-based RADAR plan position indicator from National Archives: Radar and Its Applications 1962 (public domain).)

CRTs were also used for vector monitors where the beam was directed to create a line-based image rather than using a fixed, grid-like scan pattern. Since these lines were not limited by the pixels of a (low resolution) bitmap display they could produce higher definition graphical output. This style of display was adopted for a few arcade games,&nbsp;[Asteroids](https://en.wikipedia.org/wiki/Asteroids_(video_game) "Wikipedia: Asteroids (video game)")&nbsp;is a well-known one. A much earlier game called&nbsp;[Spacewar!](https://en.wikipedia.org/wiki/Spacewar! "Wikipedia: Spacewar!")&nbsp;preceded Asteroids. Colour was introduced by Atari for the&nbsp;[Star Wars (1983)](https://en.wikipedia.org/wiki/Star_Wars_(1983_video_game) "Wikipedia: Star Wars (1983 video game)")&nbsp;arcade game but as the cost of video memory decreased and the resolution of video cards improved the demand for and use of vector displays diminished.

The first oscilloscopes used CRTs too - these are now sometimes referred to as cathode-ray oscilloscopes (CRO) to differentiate them from&nbsp;the modern&nbsp;[flat panel](https://en.wikipedia.org/wiki/Flat-panel_display "Wikipedia: flat panel display")&nbsp;digital storage oscilloscopes (DSO). Oscilloscopes are normally used for inspecting electrical signals but they can also be used to display images in various ways.

# Making oscilloscope images with DACs

## Image Creation with DAC

The "analogue" outputs on [ATmega328](https://en.wikipedia.org/wiki/ATmega328 "Wikipedia: ATmega328")-based Arduino boards&nbsp;are [PWM digital outputs](https://en.wikipedia.org/wiki/Pulse-width_modulation#Duty_cycle "Wikipedia: pulse-width modulation") rather than true analogue outputs. The faster SAMD range includes digital to analogue converters:

- [SAMD21](https://www.microchip.com/wwwproducts/en/ATSAMD21G18 "Microchip: ATSAMD21G18") (M0) - one 10bit DAC 0V-3.3V, maximum 350 kilosamples per second,
- [SAMD51](https://www.microchip.com/wwwproducts/en/ATSAMD51N19A "Microchip: ATSAMD51N19A") (M4) - two 12bit DACs 0V-3.3V, maximum 1 Megasample per second.

DACs are commonly used for audio but they can be used to create any electrical signal.&nbsp;[Adafruit Learn: Circuit Playground Express (& other ATSAMD21 Boards) DAC Hacks](https://learn.adafruit.com/circuit-playground-express-dac-hacks "Adafruit Learn: Circuit Playground Express (& other ATSAMD21 Boards) DAC Hacks")&nbsp;shows how to create low resolution composite video and an AM radio signal in C/Arduino. A compiled language with predictable execution speed is generallly more suitable for DAC output. CircuitPython can be used for high rate DAC output with the aid of a built-in library.

## Two DACs

Two analogue outputs allow control of the beam on an x-y oscilloscope. The beam needs to be moved gradually between the start and end of each line to draw a line. The large animation at the top of [Overview](https://learn.adafruit.com/dac-oscilloscope-images/overview "Overview") page shows the lines being progressively [interpolated](https://en.wikipedia.org/wiki/Interpolation "Wikipedia: interpolation")&nbsp;to form increasingly solid-looking set of lines.

[Tennis for Two](https://en.wikipedia.org/wiki/Tennis_for_Two "Wikipedia: Tennis for Two")&nbsp;(short, looping clip shown below) was a very early game in 1958 using an oscilloscope as a display.

![](https://cdn-learn.adafruit.com/assets/assets/000/078/215/medium800thumb/hacks_wikipedia-tennis-for-two-clip.jpg?1563285178 Tennis for Two (recreation) at Brookhaven National Laboratory (public domain).)

This technique works well on both a CRO and DSO.

PC audio cards feature DACs and also can be used to create interesting x-y oscilloscope output. The x-y signals can be crafted to some degree to also playback as music,&nbsp;[Jerobeam Fenderson](https://www.youtube.com/channel/UCECl4aNz5hvuRzW5fgCOHKQ)&nbsp;has produced many impressive examples of this.

## Three DACs

Some CROs have an advanced feature referred to as x-y-z mode where an additional z input can be used to control the beam intensity. This gives it capabilities similar to a black and white monitor/television.

The Asteroids arcade game used discrete intensity to vary brightness including turning the beam off between objects. This can be seen in&nbsp;[Displaying Asteroids XY on an analog oscilloscope](https://www.youtube.com/watch?v=J0DAXM0GOL8 "YouTube: Displaying Asteroids XY on an analog oscilloscope https://www.youtube.com/watch?v=J0DAXM0GOL8")&nbsp;(YouTube).

## One DAC

[Composite video](https://en.wikipedia.org/wiki/Composite_video "Wikipedia: composite video"), a descendent of the early [405](https://en.wikipedia.org/wiki/405-line_television_system "Wikipedia: 405 line television system")/[441](https://en.wikipedia.org/wiki/441-line_television_system "Wikipedia: 441 line television system") line television standards, is one way to create image/video output suitable for display on a television. The full bandwidth is around 6-8MHz [necessitating](https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem "Wikipedia: Nyquist-Shannon sampling theorem") a 12-16 megasample per second DAC!

A lower resolution image with a primitive synchronisation scheme can be used to display an image on an oscilloscope with the help of the normal timebase for the x-axis control. A CRO helps here because the brightness of the display _varies with beam deflection speed._

The images here show how the beam intensity (brightness) varies with sweep rate.

The near vertical parts of a rapidly rising signal (high slew rate) are barely visible. This is often seen on square waves. Sawtooth waves are also interesting as the ramp will be visible but the vertical part less so.

A sine wave may show some gaps if it's created with low resolution samples or the DAC has very low resolution. These are more likely to visible away from the peaks where the wave has a steeper gradient and hence the difference between each consecutive sample is larger.

![feather_cro-cpx-sine-43.jpeg](https://cdn-learn.adafruit.com/assets/assets/000/078/184/medium640/feather_cro-cpx-sine-43.jpeg?1563030527)

![feather_cro-cpx-square-43.jpeg](https://cdn-learn.adafruit.com/assets/assets/000/078/185/medium640/feather_cro-cpx-square-43.jpeg?1563030546)

![feather_cro-cpx-sawtooth-43.jpeg](https://cdn-learn.adafruit.com/assets/assets/000/078/186/medium640/feather_cro-cpx-sawtooth-43.jpeg?1563030559)

A low-resolution image can be formed by allowing the normal timebase to control the beam horizontally and using the DAC to control the vertical position. To draw an on-pixel, the DAC can be set to a value on-screen, if a pixel is not on then the DAC can be set of a value off-screen. If the slew rate is high enough the vertical transitions will be barely visible on a CRO. This technique only allows one pixel per column to be displayed as the beam scans from left to right, multiple scans need to be used to create a complete image. If the frame rate is 60Hz and the image is 50x40 pixels then the DAC needs to output samples at 60\*50\*40 = 120 kilosamples-per-second (ksps or kHz). This is far higher than the rate required for audio but still within the capabilities of the SAMD21/SAMD51 chips. A large sync pulse can be added to allow the oscilloscope to trigger (start) the horizontal scan. This is, in effect, a crude form of composite video.

This technique is likely to work well on an old&nbsp;CRO but the vertical lines will be too prominent on a DSO.

Warning: 

# Making oscilloscope images with DACs

## CircuitPython

CircuitPython is a version of the Python language that runs on a number of popular microcontroller boards.

If you are new to CircuitPython, see [Welcome to CircuitPython!](https://learn.adafruit.com/welcome-to-circuitpython "Adafruit: Welcome to CircuitPython!")

Adafruit suggests using the Mu editor to edit your code and have an interactive REPL in CircuitPython.&nbsp;[You can learn about Mu and its installation in this tutorial](https://learn.adafruit.com/welcome-to-circuitpython/installing-mu-editor).

## Libraries

This project does not require any libraries from the CircuitPython library bundle as the libraries used are built-in.

The&nbsp;[audioio](https://circuitpython.readthedocs.io/en/latest/shared-bindings/audioio/ __init__.html "CircuitPython: Docs: Core Modules: audioio")&nbsp;library has two useful characteristics/features:

- sends data to the DAC(s) at a constant, controllable rate,
- uses [direct memory access](https://en.wikipedia.org/wiki/Direct_memory_access "Wikipedia: direct memory access")&nbsp;(DMA) hardware feature of the SAMD&nbsp;[system on a chip](https://en.wikipedia.org/wiki/System_on_a_chip "Wikipedia: System on a chip")&nbsp;(SoC) which transfers data to the DAC(s), leaving the CPU free to do other things.

The DMA feature allows the CircuitPython program to run at the same time as transferring data (prepared in advance) to the DAC(s).

This code has been tested on CircuitPython&nbsp;4.1.0 Release Candidate 0. It should run on versions of CircuitPython higher than this when they are available.

# Making oscilloscope images with DACs

## One DAC

![](https://cdn-learn.adafruit.com/assets/assets/000/078/214/medium800/hacks_cpx-dac-image-twosyncpulses-2000x1500.jpg?1563283585 CPX DAC full range output showing sync pulses - voltage and DAC values on y axis.)

The libraries for manipulating image formats are too complex and bulky to run on the SAMD21 (M0) boards like the CPX. The data for the DAC is best prepared on a host computer as a (mono) [wav](https://en.wikipedia.org/wiki/WAV "Wikipedia: WAV") file.

Download the code below and the [dacanim.wav](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/CPX_DAC_Guide/dacanim.wav "dacanim.wav")&nbsp;(use **Save link as...** in browser) example wav file. Plug your CPX or other M0-based board into your computer via a known-good USB data cable. A flash drive named **CIRCUITPY** should appear in your file explorer/finder program. Copy the **dacanim.wav** and code below to the&nbsp; **CIRCUITPY** &nbsp;drive, renaming the latter to&nbsp;[code.py.](https://learn.adafruit.com/welcome-to-circuitpython/the-circuitpy-drive "Adafruit: Welcome to CircuitPython! The CIRCUITPY Drive")

[dacanim.wav](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/CPX_DAC_Guide/dacanim.wav)
Connect the CPX&nbsp; **A0** &nbsp;pad output to the an&nbsp;oscilloscope input and **GND** to ground to see the image. The trigger value may need to be set and the timebase adjusted to set the width. The bright top line is best placed off screen by adjusting the **volts/div** &nbsp;and **y position**.

Scroll past the code below for a video showing the image output.

```python
# Output prepared samples from a wav file to (CPX) DAC

import board, audioio, audiocore

dac = audioio.AudioOut(board.A0)
wav_file = open("dacanim.wav", "rb")
output_wave = audiocore.WaveFile(wav_file)
dac.play(output_wave, loop=True)
while True:
    pass
```

The image at the top of screen shows the full voltage range of the DAC on an oscilloscope running with a manual negative trigger value. The bitmap image is encoded to appear between 0.3V and 3.0V. The 3.3V level (bright line at top) is used for when there's no pixel to display, the 0V level is used for the synchronisation pulse signifying the beginning of each line.

## Oscilloscope Output Video

The video below shows the spinning logo output from a CPX connected to a Hameg HM203-6 oscilloscope. The **timebase** and **volts/div** are set so the 40x40 resolution, 50fps animation fills the screen. **&nbsp;** This is a rare occasion where a slightly unfocussed oscilloscope beam can look better as it enlarges and softens the edges of the "pixels".

https://www.youtube.com/watch?v=zxHv_-Npj3Q

## Python 3 Code

The example command line and code below for **pngtowav** can be used on a computer to generate the wav file. This code uses the&nbsp;[imageio](https://pypi.org/project/imageio/ "PyPi: imageio")&nbsp;library.

```
pngtowav -r -f 50 -o dacanim.wav logo.frame.{00..49}.png
```

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CPX_DAC_Guide/python/pngtowav.py

## Code Discussion

The code is fairly straightforward:

1. Command line argument parsing.
2. Iterate over images converting each one to the DAC representation.
3. Write single channel&nbsp; **wav** file.

The sample rate set in the wav file is based on the resolution of the images and frame rate. A 40x40 pixel image at 50 frames per second (with the sync pulse _replacing_ the first column) needs to be output at 40\*40\*50 = 80000 Hz.

# Making oscilloscope images with DACs

## Two DACs

![](https://cdn-learn.adafruit.com/assets/assets/000/078/325/medium800/hacks_pygamer-2dac-xy-lissajous-3vs1ph-2000x1500.jpg?1563448966 Classic lissajous figure using x-y oscilloscope inputs.)

## Simple Figures

[Lissajous](https://en.wikipedia.org/wiki/Lissajous_curve "Wikipedia: lissajous curve") figures are classic x-y oscilloscope imagery. They are easily created from sine waves. The short code below shows how to create the one pictured above, the same one that inspired the&nbsp;[Australian Broadcasting Corporation (ABC)](https://en.wikipedia.org/wiki/Australian_Broadcasting_Corporation "Wikipedia: Australian Broadcasting Corporation")&nbsp;logo.

```python
# Lissajous version 1
import array, math
import board, audioio, audiocore
 
length = 1000
samples_xy = array.array("H", [0] * length * 2)

# Created interleaved x, y samples
for idx in range(length):
    samples_xy[2 * idx] = round(math.sin(math.pi * 2 * idx / length) * 10000 + 10000)
    samples_xy[2 * idx + 1] = round(math.sin(math.pi * 2 * 3 * idx / length + math.pi / 2) * 10000 + 10000)

output_wave = audiocore.RawSample(samples_xy,
                                channel_count=2,
                                sample_rate=100*1000)
dacs.play(output_wave, loop=True)
while True:
   pass
```

This image, based on 1000 samples per channel, is flicker-free at 100 kHz output rate. There's some minor flicker at (audio) rates like 48 kHz.

The samples can also be output with the [analogio](https://circuitpython.readthedocs.io/en/latest/shared-bindings/analogio/ __init__.html "CircuitPython Docs: Core: Modules: analogio") library by writing a loop to assign to the two DACs. The version 2 code below shows a typical approach.

```
# Lissajous version 2
import array, math
import board, analogio
length = 1000
samples_x = array.array("H", [0] * length)
samples_y = array.array("H", [0] * length)

for idx in range(length):
    samples_x[idx] = round(math.sin(math.pi * 2 * idx / length) * 10000 + 10000)
    samples_y[idx] = round(math.sin(math.pi * 2 * 3 * idx / length + math.pi / 2) * 10000 + 10000)

dac_a0 = analogio.AnalogOut(board.A0)
dac_a1 = analogio.AnalogOut(board.A1)
    
while True:
    for x, y in zip(samples_x, samples_y):
        dac_a0.value = x
        dac_a1.value = y

```

Version 2 flickers a little at high brightness showing the performance of the interpreter is just about adequate for looping over a thousand sample pairs. However, it has very visible&nbsp;_bright_ spots appearing every second or so. These artefacts in some ways look attractive but they are not intentional and it's useful to understand why they occur.

There will be a small gap in time as the `for` loop finishes and the `while` loop executes the next `for` loop. The bright spots _move around the figure_ so this cannot be an explanation for the brief pause in beam movement. The "random" placement of the bright spot suggests something else is causing a pause in the execution of the application code leaving the beam stationary for a moment. This is probably some regular tidying of memory by the CircuitPython interpreter known as a&nbsp;[garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science) "Wikipedia: Garbage collection (computer science)")&nbsp;(GC).

```
# snippet of Lissajous version 3

while True:
    for idx in range(length):
        a0.value = samples_x[idx]
        a1.value = samples_y[idx]
```

Version 3 replaces the `for` loop with one which just iterates over the array indices rather than using the `zip()`. The bright spots have gone with this simpler code. This makes sense as this code has no need to allocate and free memory as it loops.

Another approach would be to move&nbsp;`zip()` outside the loop and only create the object _once_ to make `for` loop more efficient. This well-intentioned migration will _not work_ as it only display the figure&nbsp;_once_.&nbsp;`zip()` (in CircuitPython based on Python 3) returns an&nbsp;[iterator](https://docs.python.org/3/glossary.html#term-iterator "Python 3 Docs: iterator")&nbsp;which is designed to be used once. If the approach of constructing the two lists is maintained then `zip()` can be used to make a single list with the aid of&nbsp;`list()`. Version 4 shows this with an additional performance enhancement of removing the temporary variables in the loop and assigning to them directly.

```
# snippet of Lissajous version 4

samples_both = list(zip(samples_x, samples_y))
while True:
    for a0.value, a1.value in samples_both:
        pass
```

## Spinning Adafruit Logo

Download the **code.py** &nbsp;file with the link below and&nbsp;[adafruit\_logo\_vector.py](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/CPX_DAC_Guide/adafruit_logo_vector.py "adafruit\_logo\_vector.py")&nbsp;file. Plug your PyGamer or other M4-based board into your computer via a known-good USB data cable. A flash drive named **CIRCUITPY** should appear in your file explorer/finder program. Copy&nbsp; **code.py** and **adafruit\_logo\_vector.py** to the&nbsp; **CIRCUITPY** drive.

Connect the PyGamer **A0** output to the **x** oscilloscope input,&nbsp; **A1** to the **y** &nbsp;input and **GND** to ground to see the image. Three [short jumper cables](https://www.adafruit.com/product/1956 "Adafruit Shop: Premium Male/Male Jumper Wires - 20 x 3") will facilitate connection to a Feather female header.

Scroll past the code below for a video showing the image output.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CPX_DAC_Guide/code.py

## Oscilloscope Output Video

The video below shows the spinning logo output from a PyGamer connected to a Hameg HM203-6 oscilloscope in x-y mode. This more complex code uses the `audioio` libraries for DAC output but still features bright spots. This is probably due to the changeover between one frame's data to the next, garbage collection should not be a factor as it would not interrupt the DMA transfers. There are also some faint spots visible some of the time. These might be related to some unexplained issues with stepping, rising [slew rate](https://en.wikipedia.org/wiki/Slew_rate "Wikipedia: slew rate") on SAMD51 DACs.

https://www.youtube.com/watch?v=kueA8iqVjBc

## Code Discussion

The essence of the code is:

1. Load line data from&nbsp; **adafruit\_logo\_vector.py**
2. Apply offset correction to centre the image.
3. Interpolate lines using&nbsp;`addpoints()`&nbsp;function&nbsp;to make them appear solid.
4. Loop:
  1. Rotate image data and write DAC output for frame to an `array.array`.
  2. Output data to DAC with&nbsp;`dacs.play()`(see excerpt below).
  3. Pause for frame length.

The `array.array` type is carefully chosen as `"h"` for signed integers. For DAC output with&nbsp;`audiocore.RawSample()`, the library happens to make a _copy_ of [the data for output rather than using it in-place](https://forums.adafruit.com/viewtopic.php?f=60&t=150894&hilit=+dac "Adafruit Forums: What is the best array type for audioio.RawSample()? "). This is useful to allow the loop to efficiently reuse the same `array.array` without the risk of mixing two different frames in the (looping) DAC output.

The `play()` method is invoked with `loop=True` which leaves the frame being continuously sent to the oscilloscope until either a `stop()` or the next `play()` is executed. This is very useful for keeping the the image on the oscilloscope and avoiding any long periods where the beam is stationary.

```
dacs.play(output_wave, loop=True)
while time.monotonic() - prev_t &lt; frame_t:
    pass
if not leave_wav_looping:
    dacs.stop()
```

When the while loop terminates as time passes beyond the duration `frame_t` the code will go on to calculate the next frame (not shown) whilst continuing to send output to the DACs. If&nbsp;`leave_wav_looping` is set to `False` then DAC output will cease and there will be both considerable flicker between frames and a bright spot.

A sophisticated garbage collection system is typically better than the programmer at scheduling [concurrent vs blocking (stop the world)](https://en.wikipedia.org/wiki/Tracing_garbage_collection#Stop-the-world_vs._incremental_vs._concurrent "Wikipedia: Tracing garbage collection") collections. For this particular program, there are some opportune points to execute&nbsp;**[gc.collect()](https://circuitpython.readthedocs.io/en/latest/docs/library/gc.html?highlight=collect#gc.collect "CircuitPython: Docs: MicroPython libraries: gc")&nbsp;**to avoid less opportune scheduling. This is an area to explore.

## Making Vector Images

If an image is only available in bitmap form then it will need converting to vector form for display.&nbsp;[Inkscape](https://inkscape.org/ "Inkscape")&nbsp;is one, free, multi-platform application which can do this.

1. Select bitmap image which can be represented well with line art.
2. Load bitmap into Inkscape.
3. Vectorise - inspect and adjust result as necessary.
4. Flatten - this will convert any (bezier) curves into a series of straight lines.
5. Save as an&nbsp;[svg](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics "Wikipedia: scalable vector graphics") file.
6. Extract line data from the&nbsp; **svg** file - the **svgtopy** utility below can help with this.

The example command line and code below can read simple **svg** files and print them as lists suitable for inclusion in a CircuitPython program.

```
svgtopy &lt; logo-flattened.svg
```

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/CPX_DAC_Guide/python/svgtopy.py

# Making oscilloscope images with DACs

## Going Further

## Ideas for Areas to Explore

- Make your own vector images and animations.
- Make an old skool vector video game.
- Investigate the suitability and limitations of (capacitor smoothed) PWM outputs on SAMD21 (M0) boards for a second analog output.
- For animations in CircuitPython explore whether judicious use of `gc.collect()` can be used to improve display output.

## Related Projects

- [Circuit Playground Express (& other ATSAMD21 Boards) DAC Hacks](https://learn.adafruit.com/circuit-playground-express-dac-hacks "Adafruit Learn: Circuit Playground Express (& other ATSAMD21 Boards) DAC Hacks") (C++/Arduino).
- Trammel Hudson:
  - [Turn Your Oscilloscope Into a Vector Video Display](https://www.nycresistor.com/2012/09/03/vector-display/ "NYC Resistor: Trammel Hudson: Turn Your Oscilloscope Into a Vector Video Display")&nbsp;- same concept as two DAC approach used here but includes construction of a simple _external_ DAC pair, uses C/Arduino.
  - [Pseudorandom 09: Vector Displays](https://www.youtube.com/watch?v=zUe60Ljsc7w "YouTube: Adafruit Industries: Pseudorandom 09: Vector Displays")&nbsp;(YouTube) - "Trammell Hudson shows us the retro beauty of vector displays and recounts his adventures in hacking the Vectrex gaming console!"

- [Feather M0 Sine Wave generator using ZeroDMA](https://blog.adafruit.com/2019/07/18/feather-m0-sine-wave-generator-using-zerodma-adafruit-feather-zerodma-microchipmakes/ "Adafruit Blog: Feather M0 Sine Wave generator using ZeroDMA")

## Further Reading

- [Arcade Jason: THE MESSAGE](https://www.youtube.com/watch?v=N4w9raNXIyw "YouTube: Arcade Jason: THE MESSAGE")&nbsp;- another explanation of x-y vector graphics with a nice example of font and text.
- [Oscilloscope Music](https://www.oscilloscopemusic.com/ "Jerobeam Fenderson's Oscilloscope Music")&nbsp;- inspirational X-Y art with tutorials.
- [Neil Fraser's JavaScript x-y oscilloscope driver](https://neil.fraser.name/news/2018/01/25/ "Neil Fraser's JavaScript x-y oscilloscope driver")
- [Jed Margolin: The Secret Life of XY Monitors](http://www.jmargolin.com/xy/xymon.htm "Jed Margolin: The Secret Life of XY Monitors")&nbsp;and [The Secret Life of Vector Generators](http://www.jmargolin.com/vgens/vgens.htm "Jed Margolin: The Secret Life of Vector Generators")
- [Recreating Asteroids with Lasers](https://www.youtube.com/watch?v=FkHjG759ABY "YouTube: standupmaths - Recreating Asteroids with Lasers")&nbsp;(YouTube) - interview with&nbsp;Seb Lee-Delisle about playing Asteroids on a laser vector display.
- [Pekka Vaananen: Quake on an oscilloscope: A technical report](http://www.lofibucket.com/articles/oscilloscope_quake.html "Pekka Vaananen: Quake on an oscilloscope: A technical report")
- Instructables:
  - [Arduino Laser Show With Full XY Control](https://www.instructables.com/id/Arduino-Laser-Show-with-Full-XY-Control/ "Instructables: Arduino Laser Show With Full XY Control")&nbsp;- using speakers as cheap alternative to [galvanometers](https://en.wikipedia.org/wiki/Galvanometer#Modern_uses "Wikipedia: Galvanometer").
  - [Arduino Laser Show With Real Galvos](https://www.instructables.com/id/Arduino-Laser-Show-With-Real-Galvos/ "Instructables: Arduino Laser Show With Real Galvos")

- [Empire Leicester Square (Cinema) Laser Shows](https://www.youtube.com/watch?v=nQivA1_ajrg "YouTube: fp30e - Empire Laser Show")&nbsp;(YouTube) - video of the laser show, part of the programme in the 1990s, flicker is visible as more lines are added to image.
- [Leadfeather Blog: Max Ernst: Levity and Gravity in His Paintings, 1942-48](http://plumedeplombe.blogspot.com/2012/04/max-ernst-levity-and-gravity-in-his.html "Leadfeather Blog: Max Ernst: Levity and Gravity in His Paintings, 1942-48")&nbsp;- pendulum-made lissajous figures.


## Featured Products

### Circuit Playground Express

[Circuit Playground Express](https://www.adafruit.com/product/3333)
 **Circuit Playground Express** is the next step towards a perfect introduction to electronics and programming. We've taken the original Circuit Playground Classic and made it even better! Not only did we pack even more sensors in, we also made it even easier to...

In Stock
[Buy Now](https://www.adafruit.com/product/3333)
[Related Guides to the Product](https://learn.adafruit.com/products/3333/guides)
### Adafruit Feather M4 Express - Featuring ATSAMD51

[Adafruit Feather M4 Express - Featuring ATSAMD51](https://www.adafruit.com/product/3857)
It's what you've been waiting for, the Feather M4 Express featuring ATSAMD51. This Feather is fast like a swift, smart like an owl, strong like a ox-bird (it's half ox, half bird, OK?) This feather is powered by our new favorite chip, the **ATSAMD51J19** -&nbsp; with...

In Stock
[Buy Now](https://www.adafruit.com/product/3857)
[Related Guides to the Product](https://learn.adafruit.com/products/3857/guides)
### Adafruit PyGamer for MakeCode Arcade, CircuitPython or Arduino

[Adafruit PyGamer for MakeCode Arcade, CircuitPython or Arduino](https://www.adafruit.com/product/4242)
What&nbsp;fits in your pocket, is fully Open Source, and can run CircuitPython, MakeCode Arcade or Arduino games you write yourself? That's right, it's the **Adafruit PyGamer!** We wanted to make an entry-level gaming handheld for DIY gaming, and maybe a little...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4242)
[Related Guides to the Product](https://learn.adafruit.com/products/4242/guides)
### USB cable - USB A to Micro-B

[USB cable - USB A to Micro-B](https://www.adafruit.com/product/592)
This here is your standard A to micro-B USB cable, for USB 1.1 or 2.0. Perfect for connecting a PC to your Metro, Feather, Raspberry Pi or other dev-board or microcontroller

Approximately 3 feet / 1 meter long

Out of Stock
[Buy Now](https://www.adafruit.com/product/592)
[Related Guides to the Product](https://learn.adafruit.com/products/592/guides)

## Related Guides

- [Adafruit Circuit Playground Express](https://learn.adafruit.com/adafruit-circuit-playground-express.md)
- [Adafruit Feather M4 Express](https://learn.adafruit.com/adafruit-feather-m4-express-atsamd51.md)
- [Introducing Adafruit PyGamer](https://learn.adafruit.com/adafruit-pygamer.md)
- [Illuminated City Skyline with MakeCode](https://learn.adafruit.com/city-skyline-with-makecode-for-cpx.md)
- [Halloween Monsters with CRICKIT and Circuit Playground Express](https://learn.adafruit.com/halloween-monsters-with-crickit.md)
- [Humidity and Temperature Monitor with E-Ink Display](https://learn.adafruit.com/humidity-and-temperature-monitor-redux-e-ink-display.md)
- [Spinning Disc Step Sequencer](https://learn.adafruit.com/spinning-disc-step-sequencer.md)
- [Wind Blowing Emoji Prop](https://learn.adafruit.com/wind-face-emoji.md)
- [Drone Claw](https://learn.adafruit.com/drone-claw.md)
- [NeoPixel Bike Light](https://learn.adafruit.com/neopixel-headlight.md)
- [Ambient Color Control Pad](https://learn.adafruit.com/ambient-color-controller.md)
- [Esenciales para CircuitPython](https://learn.adafruit.com/esenciales-para-circuitpython.md)
- [¡Bienvenido a CircuitPython!](https://learn.adafruit.com/bienvenido-a-circuitpython-2.md)
- [CPX Mystery Dreidel](https://learn.adafruit.com/cpx-mystery-dreidel.md)
- [Make It Talk](https://learn.adafruit.com/make-it-talk.md)
- [Circuit Playground Express: Piano de Limones](https://learn.adafruit.com/circuit-playground-express-piano-de-limones.md)
- [DIY Robotic Sky Tracking Astrophotography Mount with CircuitPython](https://learn.adafruit.com/diy-robotic-sky-tracking-astrophotography-mount.md)
- [Installing Microsoft MakeCode for Adafruit](https://learn.adafruit.com/installing-makecode-for-adafruit.md)
