The plasma effect is a computer-based visual effect animated in real-time. It uses cycles of changing colours warped in various ways to give an illusion of liquid, organic movement.
Plasma was probably invented by demo coders for use in their demos where the effect was heavily used, especially in the early 1990s. [...] Plasma can also be implemented easily in software rendering by using sinus tables and pseudocolor palettes, and it has also been the first true demo effect for many beginning PC democoders.
Here I'll be showing only ONE way to do a plasma effect. The word "Plasma Effect" describes rather a "family" of effects, than just a specific one. There are some reference implementations, but using them will end up getting something that's missing that "new and exciting"-feature.
Last year I did a lightning talk on why I'm coding demos, today I'll explain how I coded a simple effect. At this rate, you'll be able to code your own demo at 42C3.
The implementation took place on an Atari 2600 VCS (1979). So it's integer based, things might be different when implementing on an current PC, but the principles should be very alike.
Biggest limitation: can't do full resolution effects on X axis.
What is the fastest way to do y=f(x), for x and y being [0..255]
(8-bit architecture)?
Using a 256 byte table as a function!
The sine wave function is one of the demo coders best friends. It makes movement seem natural.
Matching a single number to set of RGB values. On 8-bit hardware usually done in hardware. Sometimes it was configurable, but this rather happend with the start of the 16 bit machines, like the Amiga.
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
A simple pseudocolor palette would be a gray scale from 0 to 255.
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
$00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | $00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | ||
$00 | $00 | ||||||||||||||||
$10 | $10 | ||||||||||||||||
$20 | $20 | ||||||||||||||||
$30 | $30 | ||||||||||||||||
$40 | $40 | ||||||||||||||||
$50 | $50 | ||||||||||||||||
$60 | $60 | ||||||||||||||||
$70 | $70 | ||||||||||||||||
$80 | $80 | ||||||||||||||||
$90 | $90 | ||||||||||||||||
$A0 | $A0 | ||||||||||||||||
$B0 | $B0 | ||||||||||||||||
$C0 | $C0 | ||||||||||||||||
$D0 | $D0 | ||||||||||||||||
$E0 | $E0 | ||||||||||||||||
$F0 | $F0 | ||||||||||||||||
original PAL | reference? |
$00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | $00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | ||
$00 | $00 | ||||||||||||||||
$10 | $10 | ||||||||||||||||
$20 | $20 | ||||||||||||||||
$30 | $30 | ||||||||||||||||
$40 | $40 | ||||||||||||||||
$50 | $50 | ||||||||||||||||
$60 | $60 | ||||||||||||||||
$70 | $70 | ||||||||||||||||
$80 | $80 | ||||||||||||||||
$90 | $90 | ||||||||||||||||
$A0 | $A0 | ||||||||||||||||
$B0 | $B0 | ||||||||||||||||
$C0 | $C0 | ||||||||||||||||
$D0 | $D0 | ||||||||||||||||
$E0 | $E0 | ||||||||||||||||
$F0 | $F0 | ||||||||||||||||
original PAL | NTSC |
$00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | $00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | ||
$00 | $00 | ||||||||||||||||
$10 | $10 | ||||||||||||||||
$20 | $20 | ||||||||||||||||
$30 | $30 | ||||||||||||||||
$40 | $40 | ||||||||||||||||
$50 | $50 | ||||||||||||||||
$60 | $60 | ||||||||||||||||
$70 | $70 | ||||||||||||||||
$80 | $80 | ||||||||||||||||
$90 | $90 | ||||||||||||||||
$A0 | $A0 | ||||||||||||||||
$B0 | $B0 | ||||||||||||||||
$C0 | $C0 | ||||||||||||||||
$D0 | $D0 | ||||||||||||||||
$E0 | $E0 | ||||||||||||||||
$F0 | $F0 | ||||||||||||||||
permutated PAL | NTSC |
$00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | $00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | ||
$00 | $00 | ||||||||||||||||
$10 | $10 | ||||||||||||||||
$20 | $20 | ||||||||||||||||
$30 | $30 | ||||||||||||||||
$40 | $40 | ||||||||||||||||
$50 | $50 | ||||||||||||||||
$60 | $60 | ||||||||||||||||
$70 | $70 | ||||||||||||||||
$80 | $80 | ||||||||||||||||
$90 | $90 | ||||||||||||||||
$A0 | $A0 | ||||||||||||||||
$B0 | $B0 | ||||||||||||||||
$C0 | $C0 | ||||||||||||||||
$D0 | $D0 | ||||||||||||||||
$E0 | $E0 | ||||||||||||||||
$F0 | $F0 | ||||||||||||||||
permutated PAL | NTSC |
$00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | $00 | $02 | $04 | $06 | $08 | $0A | $0C | $0E | ||
$00 | $00 | ||||||||||||||||
$10 | $10 | ||||||||||||||||
$20 | $20 | ||||||||||||||||
$30 | $30 | ||||||||||||||||
$40 | $40 | ||||||||||||||||
$50 | $50 | ||||||||||||||||
$60 | $60 | ||||||||||||||||
$70 | $70 | ||||||||||||||||
$80 | $80 | ||||||||||||||||
$90 | $90 | ||||||||||||||||
$A0 | $A0 | ||||||||||||||||
$B0 | $B0 | ||||||||||||||||
$C0 | $C0 | ||||||||||||||||
$D0 | $D0 | ||||||||||||||||
$E0 | $E0 | ||||||||||||||||
$F0 | $F0 | ||||||||||||||||
permutated PAL with frog-dna | NTSC |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
Pixel: | ![]() |
---|---|
Frame: | ![]() |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
Pixel: | ![]() |
---|---|
Frame: | ![]() |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | ||||||||||||||||
$10 | ||||||||||||||||
$20 | ||||||||||||||||
$30 | ||||||||||||||||
$40 | ||||||||||||||||
$50 | ||||||||||||||||
$60 | ||||||||||||||||
$70 | ||||||||||||||||
$80 | ||||||||||||||||
$90 | ||||||||||||||||
$A0 | ||||||||||||||||
$B0 | ||||||||||||||||
$C0 | ||||||||||||||||
$D0 | ||||||||||||||||
$E0 | ||||||||||||||||
$F0 |
Pixel: | ![]() |
---|---|
Frame: | ![]() |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
$00 | 00 | 02 | 04 | 06 | 08 | 0A | 0C | 0E | 0E | 0C | 0A | 08 | 06 | 04 | 02 | 00 |
$10 | 20 | 22 | 24 | 26 | 28 | 2A | 2C | 2E | 2E | 2C | 2A | 28 | 26 | 24 | 22 | 20 |
$20 | 20 | 22 | 24 | 26 | 28 | 2A | 2C | 2E | 2E | 2C | 2A | 28 | 26 | 24 | 22 | 20 |
$30 | 40 | 42 | 44 | 46 | 48 | 4A | 4C | 4E | 4E | 4C | 4A | 48 | 46 | 44 | 42 | 40 |
$40 | 60 | 62 | 64 | 66 | 68 | 6A | 6C | 6E | 6E | 6C | 6A | 68 | 66 | 64 | 62 | 60 |
$50 | 80 | 82 | 84 | 86 | 88 | 8A | 8C | 8E | 8E | 8C | 8A | 88 | 86 | 84 | 82 | 80 |
$60 | A0 | A2 | A4 | A6 | A8 | AA | AC | AE | AE | AC | AA | A8 | A6 | A4 | A2 | A0 |
$70 | C0 | C2 | C4 | C6 | C8 | CA | CC | CE | CE | CC | CA | C8 | C6 | C4 | C2 | C0 |
$80 | D0 | D2 | D4 | D6 | D8 | DA | DC | DE | DE | DC | DA | D8 | D6 | D4 | D2 | D0 |
$90 | B0 | B2 | B4 | B6 | B8 | BA | BC | BE | BE | BC | BA | B8 | B6 | B4 | B2 | B0 |
$A0 | 90 | 92 | 94 | 96 | 98 | 9A | 9C | 9E | 9E | 9C | 9A | 98 | 96 | 94 | 92 | 90 |
$B0 | 70 | 72 | 74 | 76 | 78 | 7A | 7C | 7E | 7E | 7C | 7A | 78 | 76 | 74 | 72 | 70 |
$C0 | 50 | 52 | 54 | 56 | 58 | 5A | 5C | 5E | 5E | 5C | 5A | 58 | 56 | 54 | 52 | 50 |
$D0 | 30 | 32 | 34 | 36 | 38 | 3A | 3C | 3E | 3E | 3C | 3A | 38 | 36 | 34 | 32 | 30 |
$E0 | 30 | 32 | 34 | 36 | 38 | 3A | 3C | 3E | 3E | 3C | 3A | 38 | 36 | 34 | 32 | 30 |
$F0 | 20 | 22 | 24 | 26 | 28 | 2A | 2C | 2E | 2E | 2C | 2A | 28 | 26 | 24 | 22 | 20 |
Pixel: | ![]() |
---|---|
Frame: | ![]() |
$00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
$00 | 40 | 41 | 43 | 44 | 46 | 47 | 49 | 4A | 4C | 4E | 4F | 51 | 52 | 54 | 55 | 57 |
$10 | 58 | 59 | 5B | 5C | 5E | 5F | 60 | 62 | 63 | 64 | 66 | 67 | 68 | 69 | 6A | 6C |
$20 | 6D | 6E | 6F | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 76 | 77 | 78 | 79 | 79 | 7A |
$30 | 7B | 7B | 7C | 7C | 7D | 7D | 7E | 7E | 7E | 7F | 7F | 7F | 7F | 7F | 7F | 7F |
$40 | 7F | 7F | 7F | 7F | 7F | 7F | 7F | 7F | 7E | 7E | 7E | 7D | 7D | 7C | 7C | 7B |
$50 | 7B | 7A | 79 | 79 | 78 | 77 | 76 | 76 | 75 | 74 | 73 | 72 | 71 | 70 | 6F | 6E |
$60 | 6D | 6C | 6A | 69 | 68 | 67 | 66 | 64 | 63 | 62 | 60 | 5F | 5E | 5C | 5B | 59 |
$70 | 58 | 57 | 55 | 54 | 52 | 51 | 4F | 4E | 4C | 4A | 49 | 47 | 46 | 44 | 43 | 41 |
$80 | 40 | 3E | 3C | 3B | 39 | 38 | 36 | 35 | 33 | 31 | 30 | 2E | 2D | 2B | 2A | 28 |
$90 | 27 | 26 | 24 | 23 | 21 | 20 | 1F | 1D | 1C | 1B | 19 | 18 | 17 | 16 | 15 | 13 |
$A0 | 12 | 11 | 10 | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 09 | 08 | 07 | 06 | 06 | 05 |
$B0 | 04 | 04 | 03 | 03 | 02 | 02 | 01 | 01 | 01 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
$C0 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 01 | 01 | 01 | 02 | 02 | 03 | 03 | 04 |
$D0 | 04 | 05 | 06 | 06 | 07 | 08 | 09 | 09 | 0A | 0B | 0C | 0D | 0E | 0F | 10 | 11 |
$E0 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 1B | 1C | 1D | 1F | 20 | 21 | 23 | 24 | 26 |
$F0 | 27 | 28 | 2A | 2B | 2D | 2E | 30 | 31 | 33 | 35 | 36 | 38 | 39 | 3B | 3C | 3E |
![]() |
![]() |
c(x) = sin(frame + x) * 2 |
![]() |
![]() |
c(x) = sin(frame + x) * 2 |
c1(x) = sin(frame - 3*x) c2(x) = sin(frame + x) * 0.5 ("* 0.5" really is ">> 1") |
![]() |
![]() |
c1(x) = sin(frame - 3*x) c2(x) = sin(frame + x) * 0.5 ("* 0.5" really is ">> 1") |
c(x) = sin(frame - 3*x) + sin(frame + x) * 0.5 |
![]() |
![]() |
c(x) = sin(frame - 3*x) + sin(frame + x) * 0.5 | turned 90° clockwise |
![]() |
![]() |
turned 90° clockwise | Atari 2600 VCS pseudocolor table |
![]() |
![]() |
turned 90° clockwise | grayscale pseudocolor table |
![]() |
![]() |
turned 90° clockwise | Atari 2600 VCS pseudocolor table |
![]() |
![]() |
c(x,y) = sin(frame - 3*x) + sin(frame + y) * 0.5 |
![]() |
![]() |
c(x,y) = sin(frame - 3*x) + sin(frame + y) * 0.5 |
The same as on the left side, just in a 16x16 pixel raster |
Want to see the full demo?
Want to start coding on the Atari 2600 VCS?
(Presentation done with deck.js and animated GIFs.) |
![]() ![]() |
/