{"id":18810728,"url":"https://github.com/jackw01/led-control","last_synced_at":"2025-05-08T04:39:34.371Z","repository":{"id":44949944,"uuid":"151202160","full_name":"jackw01/led-control","owner":"jackw01","description":"Advanced WS2812/SK6812 RGB/RGBW LED controller with on-the-fly Python animation programming, web code editor/control interface, 1D, 2D, and 3D display support, and E1.31 sACN support","archived":false,"fork":false,"pushed_at":"2023-11-10T05:12:52.000Z","size":13653,"stargazers_count":180,"open_issues_count":18,"forks_count":39,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-08T04:39:30.704Z","etag":null,"topics":["e131","led","led-controller","led-cube","led-matrix","led-strips","raspberry-pi","rgb","rgb-led","rgbw","rgbw-leds","sacn","sk6812","ws2812b"],"latest_commit_sha":null,"homepage":"https://jackw01.github.io/led-control/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jackw01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-10-02T04:55:39.000Z","updated_at":"2025-04-24T21:46:51.000Z","dependencies_parsed_at":"2023-01-30T01:31:15.434Z","dependency_job_id":"5768debf-024d-4c50-86f9-0589c4b940ba","html_url":"https://github.com/jackw01/led-control","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackw01%2Fled-control","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackw01%2Fled-control/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackw01%2Fled-control/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackw01%2Fled-control/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jackw01","download_url":"https://codeload.github.com/jackw01/led-control/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253002778,"owners_count":21838634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["e131","led","led-controller","led-cube","led-matrix","led-strips","raspberry-pi","rgb","rgb-led","rgbw","rgbw-leds","sacn","sk6812","ws2812b"],"created_at":"2024-11-07T23:22:02.177Z","updated_at":"2025-05-08T04:39:34.336Z","avatar_url":"https://github.com/jackw01.png","language":"Python","readme":"# LED Control\n\n![screenshot.png](screenshot.png)\n\n## Features\n* Lightweight responsive web interface works on both desktop and mobile devices\n* In-browser code editor and color palette editor make creating and modifying animations easy\n* Large selection of built-in animations and color palettes means you don't have to write any code\n* Works with cheap and readily available WS281x and SK6812 LED strips and strings\n* Runs on a Raspberry Pi single-board computer directly connected to LEDs, and on any other computer with the help of a low-cost microcontroller board (Raspberry Pi Pico) connected via USB\n* Supports pixel mapping for arbitrary 2D and 3D LED arrangements\n* Seamlessly supports HSV-to-RGBW and RGB-to-RGBW conversion for RGBW LED strips\n* Supports networked E1.31 sACN DMX control for music visualization through [LedFx](https://github.com/LedFx/LedFx)\n\n### Technical Details\n* Animation patterns are defined as Python functions that work similarly to fragment shaders\n* Capable of achieving up to 150 FPS on 150 RGBW LEDs on a Raspberry Pi Zero\n* Web backend and animation code are written in Python using the [Flask](https://flask.palletsprojects.com/en/2.0.x/) web framework for ease of development and served using [bjoern](https://github.com/jonashaag/bjoern)\n* Web frontend UI is implenented using [Vue 3](https://vuejs.org/)\n* Color conversions, color correction, and final rendering operations are implemented in a C extension module for maximum performance\n\n#### Framerate Note\nThe theoretical maximum framerate for 150 RGBW LEDs is 800000 Hz / (8*4) bits / 150 = 166.67 FPS.\nAll built-in animations run at over 50FPS with 150 LEDs on a Raspberry Pi Zero, the least powerful Raspberry Pi model. The framerate is limited to 60FPS by default to reduce CPU usage.\n\n## Install\n\n### Hardware Setup (All Platforms)\nObtain a WS2812B or SK6812B LED strip (**SK6812 RGB/White LEDs are highly recommended**) and a suitable 5V power supply. USB power may be suitable, and an external power supply may not be needed, when using small numbers of LEDs (less than 100 RGBW or 50 RGB LEDs).\n\n### Hardware Setup (External LED Driver)\n1. Obtain a Raspberry Pi Pico (US$4) or any microcontroller board based on the RP2040 chip.\n2. Connect the LED strip to your microcontroller:\n    - MCU GND to LED GND\n    - MCU GPIO12 to LED Data in\n    - Power supply ground to LED GND\n    - Power supply 5V to LED 5V (Important: use the VBUS pin, not VSYS, to power LEDs from USB on Raspberry Pi Pico and Pico W boards)\n\nAny GPIO pin can be used instead of GPIO12 if the definition in `firmware/config.h` is changed appropriately.\n\n### Hardware Setup (Raspberry Pi)\n1. Obtain a Raspberry Pi single-board computer (any model). Due to the unavailability of Raspberry Pis, using any other computer with an external LED driver is recommended (see above).\n2. Connect the LED strip to your Raspberry Pi:\n    - Pi GND to LED GND\n    - Pi GPIO18 to LED Data in\n    - Power supply ground to LED GND\n    - Power supply 5V to LED 5V\n\nFor more information on which Raspberry Pi GPIO pins LED strips can be connected to, see [here](https://github.com/jgarff/rpi_ws281x).\n\n#### RGBW LEDs Are Highly Recommended\nKnow what you're doing with electricity. Addressable LEDs can draw a lot of current, especially in long strips. You should use RGBW LEDs for the reason that **they look better and require much less power** when displaying whiter colors (a good quality 5V 4A power supply can comfortably handle 150 RGBW LEDs at full brightness).\n\nFor large installations, each group of up to ~200 LEDs should be connected to the power supply with its own adequately sized wires. Don't expect to get good or safe results by daisy chaining strips together. Furthermore, when using a high current (20+A) power supply, a short circuit somewhere along the LED strip will likely not draw enough current to trip your power supply's over-current protection due to the resistance of the flexible PCB. This is a serious fire hazard, so each individually powered group of LEDs should be protected with its own fuse or circuit breaker rated to ideally no more than 10A. See [this guide](https://learn.adafruit.com/1500-neopixel-led-curtain-with-raspberry-pi-fadecandy/power-topology) for good information on powering hundreds or thousands of LEDs.\n\nAddressable LED strips usually come with seriously undersized power wires and barrel jacks or JST SM connectors rated for only 3A, and it would be a good idea to replace these appropriately.\n\nFor more information on using a level shifter, which may be necessary with some WS2812 RGB LED strips, see [this Adafruit guide](https://learn.adafruit.com/neopixels-on-raspberry-pi/raspberry-pi-wiring#using-external-power-source-without-level-shifting-3005993-11).\n\n#### If You Really Want To Use RGB LEDs\nYou should budget [at least 50mA for each LED at full brightness](https://www.pjrc.com/how-much-current-do-ws2812-neopixel-leds-really-use/), which means 7.5A for 150 LEDs (5 meters of 30 LED/m strip, 2.5m of 60LED/m strip...). In practice, your LED strips won't draw this much current, but your power supply should be capable of handling it.\n\nThe flexible PCBs and connectors used in these LED strips are not really designed to handle these currents, and begin to heat up when passing as little as 2-3A. Again, each group of up to ~150 LEDs should be powered through its own adequately sized wires.\n\n### Software Setup (With Pi Pico LED Driver)\nPython 3.7 or newer is required.\n\n1. Ensure that git, python, pip, and the Raspberry Pi Pico SDK are installed.\n2. `git clone https://github.com/jackw01/led-control.git`\n3. `cd led-control`\n4. `git checkout tags/v2.1.1`\n5. `python setup.py develop`\n6. Modify `firmware/config.h` with the correct parameters for your hardware setup.\n7. Following instructions in the Raspberry Pi Pico SDK documentation, build the firmware (located in the `firmware` folder).\n8. Flash the firmware onto your microcontroller board: hold down the BOOTSEL button when connecting it to your computer with a USB cable and it should enumerate as a USB storage device. Drag and drop or copy and paste the `.uf2` binary file in `build/firmware` into the microcontroller.\n9. Determine the serial port ID of your microcontroller. On Windows, this can be done through Device Manager.\n10. `ledcontrol --led_count NUMBER_OF_LEDS_HERE --serial_port SERIAL_PORT_HERE` (add `--led_pixel_order GRBW` if using RGBW LEDs)\n\n### Software Setup (Raspberry Pi)\nPython 3.7 or newer is required.\n\n1. `sudo apt-get install scons swig libev-dev python3-dev python3-setuptools git python3-pip`\n2. `git clone --recurse-submodules https://github.com/jackw01/led-control.git`\n3. `cd led-control`\n4. `git checkout tags/v2.1.1`\n5. `sudo python3 setup.py develop`\n6. `sudo ledcontrol --led_count NUMBER_OF_LEDS_HERE` (add `--led_pixel_order GRBW` if using RGBW LEDs)\n\n#### Common Issues\nLEDControl and the Raspberry Pi audio subsystem cannot be use together since they both use the PWM hardware. On some Linux distributions, you must disable the audio kernel module by commenting out the line `dtparam=audio=on` in `/boot/config.txt` or by creating a file `/etc/modprobe.d/snd-blacklist.conf` with the contents `blacklist snd_bcm2835`.\n\n### Command Line Configuration Arguments\nWeb server and LED hardware parameters must be specified as command line arguments when running ledcontrol. Note that none of the LED hardware-related arguments will have an effect when using a Pi Pico to drive the LEDs.\n```\nusage: ledcontrol [-h] [--port PORT] [--host HOST] [--led_count LED_COUNT]\n                  [--config_file CONFIG_FILE]\n                  [--pixel_mapping_json PIXEL_MAPPING_JSON] [--fps FPS]\n                  [--led_pin LED_PIN] [--led_data_rate LED_DATA_RATE]\n                  [--led_dma_channel LED_DMA_CHANNEL]\n                  [--led_pixel_order LED_PIXEL_ORDER]\n                  [--led_brightness_limit LED_BRIGHTNESS_LIMIT]\n                  [--save_interval SAVE_INTERVAL] [--sacn] [--hap] [--no_timer_reset]\n                  [--dev] [--serial_port SERIAL_PORT]\n\noptional arguments:\n  -h, --help            show this help message and exit\n  --port PORT           Port to use for web interface. Default: 80\n  --host HOST           Hostname to use for web interface. Default: 0.0.0.0\n  --led_count LED_COUNT\n                        Number of LEDs\n  --config_file CONFIG_FILE\n                        Location of config file. Default: /etc/ledcontrol.json\n  --pixel_mapping_json PIXEL_MAPPING_JSON\n                        JSON file containing pixel mapping (see README)\n  --fps FPS             Refresh rate limit for LEDs, in FPS. Default: 60\n  --led_pin LED_PIN     Pin for LEDs (see\n                        https://github.com/jgarff/rpi_ws281x). Default: 18\n  --led_data_rate LED_DATA_RATE\n                        Data rate for LEDs. Default: 800000 Hz\n  --led_dma_channel LED_DMA_CHANNEL\n                        DMA channel for LEDs. DO NOT USE CHANNEL 5 ON Pi 3 B.\n                        Default: 10\n  --led_pixel_order LED_PIXEL_ORDER\n                        LED color channel order. Any combination of RGB with\n                        or without a W at the end. Default: GRB, try GRBW for\n                        SK6812\n  --led_brightness_limit LED_BRIGHTNESS_LIMIT\n                        LED maximum brightness limit for the web UI. Float\n                        from 0.0-1.0. Default: 1.0\n  --save_interval SAVE_INTERVAL\n                        Interval for automatically saving settings in seconds.\n                        Default: 60\n  --sacn                Enable sACN / E1.31 support. Default: False\n  --hap                 Enable HomeKit Accessory Protocol support. Default: False\n  --no_timer_reset      Do not reset the animation timer when patterns are\n                        changed. Default: False\n  --dev                 Development flag. Default: False\n  --serial_port SERIAL_PORT\n                        Serial port for external LED driver.\n```\n\n### Color Correction\nBy default, the color correction values on the web interface are set to (255, 190, 170) which is a good starting point for SK6812 RGBW LEDs, but not all LEDs are the same. You should adjust these values as well as the global color temperature while using the \"Test Color Correction\" feature, which displays the white point of the RGB subpixels in your LEDs, even if you are using RGBW LEDs.\n\n### Built-In Color Palettes\n![palettes.png](palettes.png)\n\n### Built-In Animation Patterns\nAnimated GIF previews of all built-in animations can be seen at [animations.md](animations.md).\n\n### E1.31 sACN, Music Visualization, and LedFx Use\nLEDControl can function as a E1.31 streaming ACN receiver, allowing the connected LEDs to be directly controlled over the network. [LedFx](https://github.com/LedFx/LedFx) is recommended for music visualization over sACN.\n\n1. Follow the [instructions](https://github.com/LedFx/LedFx) to install LedFx and set up your computer's audio output.\n2. Add your LEDControl device in LedFx: Select `e131` as the device type, use the hostname or IP of your Raspberry Pi, and enter the number of LEDs you have attached.\n3. Run LEDControl with the `--sacn` command line flag. An option to enable sACN receiver mode will appear on the web interface.\n4. Enable sACN receiver mode in the LEDControl web interface.\n5. Configure a music visualizer effect in LedFx.\n\nWhile sACN receiver mode is enabled, the LED refresh rate is determined by your sACN server. There may be noticeable latency when using sACN on congested networks or if other software on the Raspberry Pi is using its network hardware; this is a known limitation of sACN.\n\n### HomeKit Accessory Protocol Support (Experimental)\nThe brightness and on/off state of LEDControl can be controlled through Apple HomeKit. Run LEDControl with the `--hap` command line flag and a setup code will be printed for manually pairing in the Apple Home app.\n\n### Pixel Mapping\nLEDControl supports pixel mapping, which allows 2- and 3-dimensional animation patterns to be mapped to any physical arrangement of LEDs. Currently, pixel mappings can only be specified with a JSON file containing an array of points representing the positions of each LED, using the `--pixel_mapping_json` command line argument. `--led_count` does not need to be specified when pixel mapping is used. The points must be in the same order that the correresponding LEDs are connected, and the units used to define the pixel mapping do not matter (negative and floating-point values are allowed).\n\n#### Example\n![pixelmapping.png](pixelmapping.png)\n\n```json\n[\n  [-3, 3, 0],\n  [0, 3, 0],\n  [3, 3, 0],\n  [3, 0, 0],\n  [3, -3, 0],\n  [0, -3, 0],\n  [-3, -3, 0],\n  [-3, 0, 0]\n]\n```\n\n## Animation Editing\nAnimation patterns are defined as Python functions that work similarly to GLSL fragment shaders or DirectX pixel shaders. The LEDControl web interface allows editing and creation of patterns using a subset of Python.\n\nPatterns are compiled using [RestrictedPython](https://github.com/zopefoundation/RestrictedPython) and run with a restricted set of builtin functions and global variables. This should prevent filesystem access and code execution, but the scripting system **should not be considered completely secure** and the web interface **should not be exposed to untrusted users**.\n\n### Pattern Function Guide\nEach animation frame, the pattern function is called once per LED/pixel with time, position, and previous state as inputs to determine the next color of that pixel.\n\n```python\n# cycle_hue_1d\ndef pattern(t, dt, x, y, z, prev_state):\n    return (t + x, 1, 1), hsv\n```\n\n#### Arguments\n##### `t`\nTime in cycles (an arbitary unit that represents one animation cycle as a floating point number). Calculated by multiplying real time in seconds by animation speed in Hz (cycles/second). Time is reset to 0 every week (168 hours) of uptime to prevent any math/overflow issues that may occur. By default, time is also reset when the animation pattern is changed or recompiled, but this behavior can be changed with the `--no_timer_reset` command-line flag.\n\n##### `dt`\nDelta time in cycles.\n\n##### `x`, `y`, `z`\nNormalized (0 to 1) value representing the position of the current LED in arbitrary units (after mapping LED indices to positions and scaling). By default, LEDs are mapped to the x axis only. One position unit represents the scale factor multiplied by the length of the axis. At a scale of less than 1, one position unit represents a fraction of the axis length and the animation is tiled to fill all the LEDs.\n\n##### `prev_state`\nPrevious color state of the current LED as an HSV or RGB tuple. Initialized to `(0, 0, 0)` when the program starts.\n\n#### Return Values\nPattern functions must return a color in tuple form and either `hsv` or `rgb` depending on the format of the color. All values are expected to be in the 0 to 1 range, except for hue. Hue values less than 0 or greater than 1 will wrap. RGB values will be clamped to the 0 to 1 range.\n\n### Supported Python Globals\n* Builtins: `None`, `False`, `True`, `abs`, `bool`, `callable`, `chr`, `complex`, `divmod`, `float`, `hash`, `hex`, `id`, `int`, `isinstance`, `issubclass`, `len`, `oct`, `ord`, `pow`, `range`, `repr`, `round`, `slice`, `str`, `tuple`, `zip`\n* All functions and constants from the [`math` module](https://docs.python.org/3/library/math.html)\n* All functions from the [`random` module](https://docs.python.org/3/library/random.html)\n\n### Color Palette Access\nColor palettes are interpolated in the HSV color space. 1000 interpolated values are stored in a lookup table to allow for fast access to any color in the palette.\n\n##### `palette(t)`\nReturns the color from the current palette corresponding to a value `t` between 0 and 1. Values of `t` less than 0 or greater than 1 will wrap.\n\n##### `palette_mirrored(t)`\nReturns a color from a mirrored version of the current palette that wraps seamlessly. Functionally equivalent to `palette(wave_triangle(t))`, but performs just as well as `palette(t)`.\n\n### Wave Functions\nAll waveforms have a period of 1 time unit, a range from 0 to 1, and a peak (`f(t)=1`) at `t=0`. When running on Raspberry Pi, optimized C implementations of these functions are used which gives a suprisingly significant performance improvement over Python.\n\n##### `wave_sine(t)`\nReturns the instantaneous value of a 1Hz sine wave at time `t`.\n\n##### `wave_cubic(t)`\nReturns the instantaneous value of a 1Hz sine wave approximated with cubic easing at time `t`. Appears to spend more time near 0 and 1 than a true sine wave.\n\n##### `wave_triangle(t)`\nReturns the instantaneous value of a 1Hz triangle wave at time `t`.\n\n##### `wave_pulse(t, duty_cycle)`\nReturns the instantaneous value of a 1Hz pulse wave of the specified duty cycle (range 0 to 1) at time `t`.\n\n##### `plasma_sines_octave(x, y, t, octaves, lacunarity, persistence)`\nCustom optimized \"plasma\" implementation that returns a sum of several octaves of sinusoid-based waveforms creating a non-random noise effect. Essentially fractal noise, but using sinusoid functions as a basis instead of pseudorandom noise. This creates more detailed and better looking plasma effects than simpler implementations. For each successive octave, the frequency (how fast the wave changes with space and time) is multiplied by `lacunarity`, and the amplitude is multiplied by `persistence`. `octaves` must be an integer. See the usage notes below.\n\n##### `plasma_sines(x, y, t, coeff_x, coeff_y, coeff_x_y, coeff_mag_xy)`\nBasic optimized function for creating RGB plasma animations (see [https://www.bidouille.org/prog/plasma](https://www.bidouille.org/prog/plasma)). Returns `sin((x + t) * coeff_x) + sin((y + t) * coeff_y) + sin((x + y + t) * coeff_x_y) + sin((sqrt(x * x + y * y) + t) * coeff_mag_xy)`. Not recommended unless you want it, `plasma_sines_octave` generally looks better.\n\n##### `perlin_noise_3d(x, y, z)`\nStandard 3D perlin noise. Use time as one of the arguments to make the noise vary with time. Returns a value from 0 to 1.\n\n##### `fbm_noise_3d(x, y, z, octaves, lacunarity, persistence)`\nStandard 3D fBm (fractal Brownian motion) noise.\n\n### Additional Utility Functions\n##### `clamp(x, min, max)`\nReturns `min` if `x \u003c min` and `max` if `x \u003e max`, otherwise returns `x`.\n\n##### `fract(x)`\nReturns the floating point component of `x` (`x - floor(x)`).\n\n##### `impulse_exp(t)`\nAsymmetrical exponential \"impulse\" wave function `f(t) = t * e^(1-t)`. Peaks at `t=1`.\n\n##### `blackbody_to_rgb(kelvin)`\nReturns a normalized RGB tuple for a color temperature in Kelvin.\n\n##### `blackbody_correction_rgb(rgb, kelvin)`\nTints an RGB color (normalized RGB tuple) to a color temperature in Kelvin. Returns a normalized RGB tuple.\n\n### Using Plasma Utility Functions\n`plasma_sines` and `plasma_sines_octave` both return the instantaneous value of a 2D periodic function at time `t` and position (`x`, `y`). The typical way of converting this value to a color is to assign the RGB values to expressions of the form `a * wave_sine(b * x + c) + d`, where `wave_sine` is a function that returns a sinusoid waveform with range 0 to 1. This is done in the code for several built in patterns.\n\nBelow is GLSL code for testing and experimenting with these plasma functions in 2D. This code can be easily run on [Shadertoy](https://www.shadertoy.com/new).\n\n##### `plasma_sines`\n```GLSL\nfloat wave_sine(float t) {\n  return cos(6.283 * t) / 2.0 + 0.5;\n}\n\nfloat plasma_sines(float x, float y, float t,\n                   float coeff_x, float coeff_y,\n                   float coeff_x_y, float coeff_dist_xy) {\n  float v = 0.0;\n  v += sin((x + t) * coeff_x);\n  v += sin((y + t) * coeff_y);\n  v += sin((x + y + t) * coeff_x_y);\n  v += sin((sqrt(x * x + y * y) + t) * coeff_dist_xy);\n  return v;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n  // Normalized pixel coordinates (from 0 to 1)\n  vec2 uv = fragCoord / iResolution.xy;\n\n  float v = plasma_sines(uv.x, uv.y, iTime, 1.0, 0.5, 0.5, 1.0);\n\n  float r = 0.8 - wave_sine(v);\n  float g = wave_sine(v + 0.333) - 0.2;\n  float b = 0.8 - wave_sine(v + 0.666);\n\n  fragColor = vec4(r, g, b, 1.0);\n}\n```\n\n##### `plasma_sines_octave`\n```GLSL\nfloat wave_sine(float t) {\n  return cos(6.283 * t) / 2.0 + 0.5;\n}\n\nfloat plasma_sines_octave(float x, float y, float t,\n                          int octaves,\n                          float temporal_freq_persistence,\n                          float amplitude_persistence) {\n  float vx = x;\n  float vy = y;\n  float spatial_freq = 1.0;\n  float temporal_freq = 1.0;\n  float amplitude = 1.0;\n  for (int i = 0; i \u003c octaves; i++) {\n    float vx1 = vx;\n    vx += cos(vy * spatial_freq + t * temporal_freq) * amplitude;\n    vy += sin(vx1 * spatial_freq + t * temporal_freq) * amplitude;\n    spatial_freq *= 2.0;\n    temporal_freq *= temporal_freq_persistence;\n    amplitude *= amplitude_persistence;\n  }\n  return vx / 2.0;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n  // Normalized pixel coordinates (from 0 to 1)\n  vec2 uv = fragCoord / iResolution.xy;\n\n  float v = plasma_sines_octave(uv.x, uv.y, iTime, 8, 1.5, 0.5);\n\n  float r = 0.8 - wave_sine(v);\n  float g = wave_sine(v + 0.333) - 0.2;\n  float b = 0.8 - wave_sine(v + 0.666);\n\n  fragColor = vec4(r, g, b, 1.0);\n}\n```\n\n## Development\nTo build the C extension module (on Raspberry Pi single-board computers only):\n```\nswig -python ./ledcontrol/driver/ledcontrol_rpi_ws281x_driver.i \u0026\u0026 sudo python3 setup.py develop\n```\n\n## License\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackw01%2Fled-control","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjackw01%2Fled-control","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackw01%2Fled-control/lists"}