SkillAgentSearch skills...

Striptease

Sexy, audio-responsive effects on LED strips.

Install / Use

/learn @lpaolini/Striptease
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Striptease

Sexy, audio-responsive effects on LED strips.

For Teensy 4 with Audio Adapter Board, by PJRC.

Quick demo

Shooting video of LEDs is very tricky. Getting the right exposure, focus and colors is not easy at all. I did my best, but the live effect from my couch is simply not comparable with what you get on video. I think the main reason is frame rate. This video has been shot at 60 fps, but the real animation runs at about 170 fps...

Effect showcase (version 3, April 2021)

Intro

I love lights, especially LED lights.

The main goals of this library are:

  • to simplify the development of visual effects to be rendered on LED strips, by providing useful abstractions and helpers
  • to provide runtime support for rendering simultaneous effects on multiple strips, controlling the transitions and adjusting parameters in real time

Credits

This work has been inspired by some very cool projects published on the Cine-Lights YouTube channel. Some of the effects still keep the same name as originally given by the author, even though they have been reimplemented from scratch and might look very different.

Note: at some point, the author decided to go closed-source, distributing compiled (.hex) files only.

Hardware

Overview

Teensy

This library is designed around the awesome Teensy 4 Development Board, available as Teensy 4.0 or Teensy 4.1, which has been chosen for a number of reasons:

  • It's an extremely capable Arduino-compatible board, running an ARM Cortex-M7 at 600 MHz with a floating point math unit, 64 & 32 bits;
  • It can be combined with the Audio Adapter Board for audio I/O and processing;
  • It comes with a large set of libraries including two great ones by Paul Stoffregen (Audio and WS2812Serial), described below;
  • It's relatively cheap, compared to what it offers.

Audio Board

The Audio Adapter Board rev. D (specific for Teensy 4.x) provides CD-quality stereo ADC/DAC and hardware processing of audio signals. An optional microphone can be soldered to the board to provide software-switchable mono microphone input, in addition to stereo line input.

Teensy 4.0 provides five serial ports suitable for non-blocking LED driving, while Teensy 4.1 provides a total of eight. However, when using the Audio Adapter Board, Serial2 and Serial5 are unavailable, leaving us with three channels for Teensy 4.0 and six for Teensy 4.1.

Audio Board - top

Audio Board - bottom

Level shifter

Teensy 4 runs at 3.3v and all I/O ports operate exclusively at this voltage, thus a level shifter (74HCT245) is required to reliably drive WS2812B LEDs with 5v signals.

Main board

I've designed a couple of custom PCBs, available in the hardware directory: one for Teensy 4.0, the other one for Teensy 4.1, housing connectors (power, IR receiver and LEDs), the DIL socket for the 74HCT245 IC, and a pair of stripline sockets for the Teensy, plus a few passive components. The Audio Adapter Board is sandwiched between the main board and the Teensy using long-terminal stripline connectors soldered to the Audio Adapter Board.

PCB

Board

Assembled - bottom

Assembled - front

Disassembled 1

Disassembled 2

PCBs have been designed using EAGLE PCB and fabricated by JLCPCB.

Main board connectors - Teensy 4.0 version (3 channels)

  • POWER: Power (GND, +5v)
  • IR-RECV: IR receiver (GND, Data, +3.3v)
  • LED1-4: LEDs (CH1, CH2, CH3, CH3)

Note: CH3 is repeated because there are only three independent outputs

Main board connectors - Teensy 4.1 version (6 channels)

  • POWER: Power (GND, +5v)
  • IR-RECV: IR receiver (GND, Data, +3.3v)
  • PROGR: Program button
  • LED1-4: LEDs (CH1, CH2, CH3, CH4)
  • LED5-8: LEDs (CH5, CH6, CH6, CH6)

Note: CH6 is repeated because there are only six independent outputs

WS2812B LED strips

For my projects I prefer high density WS2812B LED strips (144 LED/m) with semi-transparent diffuser, because they look amazing at short distance. I'm providing photos of the diffuser on top of a printed page, to give you an idea of the transparency.

Diffuser - top

Diffuser - bottom

Rail

Update rate (FPS)

Strips can be any length and they don't need to be matched. However, being channels driven in parallel, the global update rate is the update rate of the longest strip. Update rate can be calculated multiplying the time required for transmitting RGB data for a single WS2812B LED (30us) by the number of the LEDs in the strip. With 6 channels available, up to 3324 LEDs can be driven at 60fps, or up to 1662 at 120fps. In my home application (about 800 LEDs), the longest strip has 192 LEDs, which translates to about 170fps.

Powering LEDs

Please be aware that the power rails on the strips have a non-negligible resistance, which would inevitably cause a voltage drop over distance. The higher the current, the higher the voltage drop (ohm's law). Total current is the sum of the current flowing through individual LEDs, which in turn depends on the RGB values. So, depending on the instantaneous state of the LEDs in the strip, the voltage drop could be enough to cause malfunctioning. To overcome this problem, you might need to inject power also at the end of the strip and, if it's very long, every n LEDs (n to be determined).

Personally, I never had to do this, even when driving 240 LEDs at full brightness, but copper thickness of the power rails might differ from one producer to another.

Power supply

I suggest using excellent quality power supplies. A faulty one can easily destroy your hardware and can even become a threat for your life!

One of my favorite brands is Traco Power.

Connectors

For connecting strips to the controller I use professional Neutrik speakON connectors: NL4FX on the cable and NL4MP on the controller.

They are rugged, super reliable connectors designed for connecting audio amplifiers to speakers, but they work amazingly well for this purpose too. Current rating is 40A (continuous) and they have four contacts, so one connector can bring power and signals to two strips using a 4-wire cable.

Infrared receiver

Any common infrared receiver, like TSOP4838 or similar, would be fine. In my projects I'm using an external one (search for "infrared extender cable"), as they usually come with a convenient red filter which increases the sensitivity by removing unwanted wavelengths.

IR receiver

Software

Code is built around four awesome libraries:

  • FastLED, for driving LED strips
  • WS2812Serial, for non-blocking driving of WS28128B LEDs
  • Audio, for sophisticated real time processing of audio signal
  • IRMP, for decoding IR remote controller codes (basically any spare remote can be adapted)

Architecture

Code has been crafted carefully, splitting responsibilites across a number of classes and introducing several useful abstractions.

Teensy 4 is an extremely powerful platform, providing plenty of RAM, flash, CPU and hardware-accelerated floating point math. For this reason, this library uses floating point math whenever it makes sense, trading unnecessary optimization for code integrity and readibility.

All the effects provided by the library (40+) are self-contained (all state is in own private class members), don't depend on strip length (they adapt to strip length or use normalized addressing) and don't depend on main loop frequency (they rely on timers so that animation speed doesn't depend on global update rate).

API

Strip

Strip is the abstract class for strip implementations (below), providing convenience methods for absolute (integer, 0 to pixel count - 1) or normalized (double, 0 to 1) LED addressing. It makes it easier to manipulate strips in a length-agnostic way.

PhysicalStrip<uint16_t SIZE, uint8_t PIN, uint16_t DENSITY>()

PhysicalStrip wraps a FastLED CRGBSet, i.e. a physical strip connected to a pin on the Teensy board.

ReversedStrip (Strip *strip)

ReversedStrip wraps an instance of Strip for reversing its pixel order.

Example

Strip A = PhysicalStrip(...) => [1, 2, 3]
Strip B = ReversedStrip(A)   => [3, 2, 1]

JoinedStrip (Strip *strip, Strip *strip2, int16_t gap = 0)

JoinedStrip wraps two instances of Strip into a single virtual strip, with an optional gap, i.e. the number of missing LEDs between the two strips.

Example 1 - join two left-to-right strips into a single left-to-right strip

Strip A = PhysicalStrip(...) => [A1, A2, A3, A4]
Strip B = PhysicalStrip(...) => [B1, B2, B4, B4, B5, B6]
Strip C = JoinedStrip(A, B)  => [A1, A2, A3, A4, B1, B2, B3, B4, B5, B6]

Example 2 - join one right-to-left strip with a left-to-right one into a single left-to-right strip

Strip A = PhysicalStrip(...) => [A5, A4, A3, A2, A1] // right-to-left
Strip B = PhysicalStrip(...) => [B1, B2, B3, B4, B5] // left-to-right
Strip C = 
View on GitHub
GitHub Stars54
CategoryDevelopment
Updated4mo ago
Forks6

Languages

C++

Security Score

97/100

Audited on Nov 2, 2025

No findings