Seeing Yourself Seeing Yourself, a circular buffer for live video
DeepDream Vision Quest was an immersive video installation, but during long exhibition runs it had a failure mode: after about five minutes with no one in view, the system would settle on a single frame, and the piece would stop feeling alive. I designed a circular video buffer to turn idle time into an attract loop, keeping the experience friendly and inviting.
Seeing Yourself Seeing Yourself, a circular buffer for live video
DeepDream Vision Quest was an immersive video installation, but during long exhibition runs it had a failure mode: after about five minutes with no one in view, the system would settle on a single frame, and the piece would stop feeling alive. I designed a circular video buffer to turn idle time into an attract loop, keeping the experience friendly and inviting.
On the left is the live webcam feed, and on the right is the circular buffer recycling captured frames in real time.
Creative Coding Gary Boodhoo
On the left is the live webcam feed, and on the right is the circular buffer recycling captured frames in real time.
Creative Coding Gary Boodhoo
On the left is the live webcam feed, and on the right is the circular buffer recycling captured frames in real time.
Creative Coding Gary Boodhoo
On the left is the live webcam feed, and on the right is the circular buffer recycling captured frames in real time.
Creative Coding Gary Boodhoo
Part memory, part anticipation. Frippertronics for the living room
Context
Understanding the problem
Context
Understanding the problem
Context
Understanding the problem
Context
Understanding the problem
The DeepDream Vision Quest installation code was written in Python 2.6, using OpenCV for live video capture and NumPy arrays as image buffers. I already knew how to grab frames from the webcam, store them, and transform them. The challenge was figuring out how to turn that memory into something that felt alive and continuous, not just a frozen snapshot of the last person who walked by. I sketched grids of frames to work out how new frames replaced old ones, how the buffer wrapped around, and how playback could start anywhere in the sequence.
The DeepDream Vision Quest installation code was written in Python 2.6, using OpenCV for live video capture and NumPy arrays as image buffers. I already knew how to grab frames from the webcam, store them, and transform them. The challenge was figuring out how to turn that memory into something that felt alive and continuous, not just a frozen snapshot of the last person who walked by. I sketched grids of frames to work out how new frames replaced old ones, how the buffer wrapped around, and how playback could start anywhere in the sequence.
The DeepDream Vision Quest installation code was written in Python 2.6, using OpenCV for live video capture and NumPy arrays as image buffers. I already knew how to grab frames from the webcam, store them, and transform them. The challenge was figuring out how to turn that memory into something that felt alive and continuous, not just a frozen snapshot of the last person who walked by. I sketched grids of frames to work out how new frames replaced old ones, how the buffer wrapped around, and how playback could start anywhere in the sequence.
The DeepDream Vision Quest installation code was written in Python 2.6, using OpenCV for live video capture and NumPy arrays as image buffers. I already knew how to grab frames from the webcam, store them, and transform them. The challenge was figuring out how to turn that memory into something that felt alive and continuous, not just a frozen snapshot of the last person who walked by. I sketched grids of frames to work out how new frames replaced old ones, how the buffer wrapped around, and how playback could start anywhere in the sequence.

This sketch helped me reason about how the buffer overwrites old frames while keeping playback continuous.
Design Notes Gary Boodhoo, Skinjester Studio

This sketch helped me reason about how the buffer overwrites old frames while keeping playback continuous.
Design Notes Gary Boodhoo, Skinjester Studio

This sketch helped me reason about how the buffer overwrites old frames while keeping playback continuous.
Design Notes Gary Boodhoo, Skinjester Studio

This sketch helped me reason about how the buffer overwrites old frames while keeping playback continuous.
Design Notes Gary Boodhoo, Skinjester Studio
Performing WIth Memories
Finding the right solution
Performing WIth Memories
Finding the right solution
Performing WIth Memories
Finding the right solution
Performing WIth Memories
Finding the right solution
I realized that to keep adding new frames to a fixed-size buffer, I had to drop the oldest ones in a rotating sequence, like a tape loop. At first, I imagined a playhead moving across the loop. Later, I realized the playhead was really a window into the natural rotation of the array, showing whatever index happened to be there.
Once I figured out that the array and window could be offset independently, the looping behavior became expressive through modulation techniques:
Offsets: starting playback at different points so the loop felt less repetitive
Oscillators: using sine, sawtooth, or square waves to modulate playback start and stop points, creating a breathing, cyclical motion
Repeaters: controlling how many times a frame or set of frames would repeat before advancing, creating rhythmic or glitchy effects
I realized that to keep adding new frames to a fixed-size buffer, I had to drop the oldest ones in a rotating sequence, like a tape loop. At first, I imagined a playhead moving across the loop. Later, I realized the playhead was really a window into the natural rotation of the array, showing whatever index happened to be there.
Once I figured out that the array and window could be offset independently, the looping behavior became expressive through modulation techniques:
Offsets: starting playback at different points so the loop felt less repetitive
Oscillators: using sine, sawtooth, or square waves to modulate playback start and stop points, creating a breathing, cyclical motion
Repeaters: controlling how many times a frame or set of frames would repeat before advancing, creating rhythmic or glitchy effects
I realized that to keep adding new frames to a fixed-size buffer, I had to drop the oldest ones in a rotating sequence, like a tape loop. At first, I imagined a playhead moving across the loop. Later, I realized the playhead was really a window into the natural rotation of the array, showing whatever index happened to be there.
Once I figured out that the array and window could be offset independently, the looping behavior became expressive through modulation techniques:
Offsets: starting playback at different points so the loop felt less repetitive
Oscillators: using sine, sawtooth, or square waves to modulate playback start and stop points, creating a breathing, cyclical motion
Repeaters: controlling how many times a frame or set of frames would repeat before advancing, creating rhythmic or glitchy effects
I realized that to keep adding new frames to a fixed-size buffer, I had to drop the oldest ones in a rotating sequence, like a tape loop. At first, I imagined a playhead moving across the loop. Later, I realized the playhead was really a window into the natural rotation of the array, showing whatever index happened to be there.
Once I figured out that the array and window could be offset independently, the looping behavior became expressive through modulation techniques:
Offsets: starting playback at different points so the loop felt less repetitive
Oscillators: using sine, sawtooth, or square waves to modulate playback start and stop points, creating a breathing, cyclical motion
Repeaters: controlling how many times a frame or set of frames would repeat before advancing, creating rhythmic or glitchy effects

I realized the buffer wasn’t a line but a circle, which made modulation and variation possible.
Design Notes Gary Boodhoo, Skinjester Studio

I realized the buffer wasn’t a line but a circle, which made modulation and variation possible.
Design Notes Gary Boodhoo, Skinjester Studio

I realized the buffer wasn’t a line but a circle, which made modulation and variation possible.
Design Notes Gary Boodhoo, Skinjester Studio

I realized the buffer wasn’t a line but a circle, which made modulation and variation possible.
Design Notes Gary Boodhoo, Skinjester Studio

Computer science textbook diagram of my apparent "discovery"
Design Notes Gary Boodhoo, Skinjester Studio

Computer science textbook diagram of my apparent "discovery"
Design Notes Gary Boodhoo, Skinjester Studio

Computer science textbook diagram of my apparent "discovery"
Design Notes Gary Boodhoo, Skinjester Studio

Computer science textbook diagram of my apparent "discovery"
Design Notes Gary Boodhoo, Skinjester Studio
Expressive Range
Part memory, part anticipation
Expressive Range
Part memory, part anticipation
Expressive Range
Part memory, part anticipation
Expressive Range
Part memory, part anticipation
Watching yourself on a short delay, especially on a large display (the bigger the better), creates a feedback loop that is part memory and part anticipation. A kind of Frippertronics for the living room.
Longer delays made the playback feel detached, like surveillance footage. Shorter delays were the most compelling, creating an out-of-body sensation and pulling people into the installation, exactly the behavior I was hoping for.
Watching yourself on a short delay, especially on a large display (the bigger the better), creates a feedback loop that is part memory and part anticipation. A kind of Frippertronics for the living room.
Longer delays made the playback feel detached, like surveillance footage. Shorter delays were the most compelling, creating an out-of-body sensation and pulling people into the installation, exactly the behavior I was hoping for.
Watching yourself on a short delay, especially on a large display (the bigger the better), creates a feedback loop that is part memory and part anticipation. A kind of Frippertronics for the living room.
Longer delays made the playback feel detached, like surveillance footage. Shorter delays were the most compelling, creating an out-of-body sensation and pulling people into the installation, exactly the behavior I was hoping for.
Watching yourself on a short delay, especially on a large display (the bigger the better), creates a feedback loop that is part memory and part anticipation. A kind of Frippertronics for the living room.
Longer delays made the playback feel detached, like surveillance footage. Shorter delays were the most compelling, creating an out-of-body sensation and pulling people into the installation, exactly the behavior I was hoping for.
Using the Frame-Blending feature while buffering live video (I pointed my webcam at the TV)
DeepDream Vision Quest runtime
Using the Frame-Blending feature while buffering live video (I pointed my webcam at the TV)
DeepDream Vision Quest runtime
Using the Frame-Blending feature while buffering live video (I pointed my webcam at the TV)
DeepDream Vision Quest runtime
Using the Frame-Blending feature while buffering live video (I pointed my webcam at the TV)
DeepDream Vision Quest runtime
Using the playhead cycling feature to show "memories" of a participant's movements
DeepDream Vision Quest runtime
Using the playhead cycling feature to show "memories" of a participant's movements
DeepDream Vision Quest runtime
Using the playhead cycling feature to show "memories" of a participant's movements
DeepDream Vision Quest runtime
Using the playhead cycling feature to show "memories" of a participant's movements
DeepDream Vision Quest runtime
Ready to play
Integration
Ready to play
Integration
Ready to play
Integration
Ready to play
Integration
I wrote a dedicated Buffer class for the feature, utilizing a 4D array and numpy.roll() to shift frame order efficiently while continuing to write new frames in real time. This made using the feature with my installation's existing signal flow pretty easy, and gave the feature legs for future applications.
Early commits got the core frame storage and cycling working
Refinements eliminated glitches and kept playback continuous
Slow-shutter blending and frame averaging features added for ghost trails and softness
Cleanup and modularization for integration with the production branch of my codebase
I wrote a dedicated Buffer class for the feature, utilizing a 4D array and numpy.roll() to shift frame order efficiently while continuing to write new frames in real time. This made using the feature with my installation's existing signal flow pretty easy, and gave the feature legs for future applications.
Early commits got the core frame storage and cycling working
Refinements eliminated glitches and kept playback continuous
Slow-shutter blending and frame averaging features added for ghost trails and softness
Cleanup and modularization for integration with the production branch of my codebase
I wrote a dedicated Buffer class for the feature, utilizing a 4D array and numpy.roll() to shift frame order efficiently while continuing to write new frames in real time. This made using the feature with my installation's existing signal flow pretty easy, and gave the feature legs for future applications.
Early commits got the core frame storage and cycling working
Refinements eliminated glitches and kept playback continuous
Slow-shutter blending and frame averaging features added for ghost trails and softness
Cleanup and modularization for integration with the production branch of my codebase
I wrote a dedicated Buffer class for the feature, utilizing a 4D array and numpy.roll() to shift frame order efficiently while continuing to write new frames in real time. This made using the feature with my installation's existing signal flow pretty easy, and gave the feature legs for future applications.
Early commits got the core frame storage and cycling working
Refinements eliminated glitches and kept playback continuous
Slow-shutter blending and frame averaging features added for ghost trails and softness
Cleanup and modularization for integration with the production branch of my codebase

GitKraken made iteration legible by helping me branch freely and keep the production branch stable
Design Notes Gary Boodhoo, Skinjester Studio

GitKraken made iteration legible by helping me branch freely and keep the production branch stable
Design Notes Gary Boodhoo, Skinjester Studio

GitKraken made iteration legible by helping me branch freely and keep the production branch stable
Design Notes Gary Boodhoo, Skinjester Studio

GitKraken made iteration legible by helping me branch freely and keep the production branch stable
Design Notes Gary Boodhoo, Skinjester Studio
Outcome


