PICO-8 Tweetcart Studies

Back to main menu


In the land of BSOD

Author: Michał Rostocki (@von_rostock)
Link: https://twitter.com/von_rostock/status/1327057028825698304

In the land of BSOD

Display Palette

0
-4
12
6
7
7
7
-13
3
-5
-6

Summary

This lovely tweetcart is actually a fairly simple effect - just two plasmas! A fairly tame plasma for the hill, and a more intense one for the sky (each one set to use different colours).

If you’re unfamiliar with plasmas, it’s an effect where you keep throwing different sin, cos, and similar functions on top of each other until you get a random-looking, but contiguous, output. See the plasmas page for some more info on this effect.

This tweetcart does a lot of processing on each pixel. Each loop only sets the colour of a single random pixel, so it takes some time to get every pixel on the screen filled with a real value.

Most pico-8 games call cls() at the start of every frame, but this effect doesn’t really define ‘frames’ and just keeps drawing new pixels on top of the screen. Because which pixel is drawn is chosen at random, it takes a while to hit every pixel on the screen when starting up. And because the screen will always be in a good-looking state, there’s no need to ever call flip(). See the cls-vs-dithering page for some more info and examples of this.

Pictures

When starting up it takes a while to hit every pixel on the screen.

Using ▤ instead of ▒ for the fill pattern.

Rendering a single complete frame. We don't see the dithering that's present in the full effect because we're not rendering any pixels on top.

The effect with i=0,2 instead of the regular i=0,6. Less iterations and layers of sin()s means a more basic gradient for the plasma.

Tweet code

cls()fillp(▒\1)pal({-4,12,6,7,7,7,-13,3,-5,-6},1)s=sin::_::x=rnd(128)m=78+s(x/192)*8y=rnd(96)w=x/6
if(y<m)n=1w=x/2+t()*2
g=y/512k=0for i=0,6do g*=2k+=.6^i*(s(s(g/2)/2-g*w/y)+s(g))end
k=abs(k^3)+7
if(y<m)k=min(k/4-7/4,4)+1
if(y>93)k=1
pset(x,16+y,k+(k+.5)\1*16)goto _

Breakdown

cls()
fillp(▒\1)
pal({-4,12,6,7,7,7,-13,3,-5,-6},1)

-- start drawing a pixel
::_::
-- which random pixel are we rendering?
x=rnd(128)
y=rnd(96)
-- this is the curve of the hill, just a gentle sin()
m=78+sin(x/192)*8

-- here's where we generate the patterns for the
--  mountains and clouds.
-- this effect is basically a bunch of sin()s tossed on
--  top of each other (same as a normal plasma).
--
-- w is the X input to the plasma. however, it also
--  affects the intensity of the end result (a higher
--  'w' will result in more colours at the higher-end
--  of the result). the ground is x/6 - pretty small so
--  you get a long pattern. the sky is x/2 - much larger
--  so the pattern gets condensed and is more intense,
--  which is compensated for by adding the extra couple
--  of white colours to the display palette :D
--
-- g is the 'density' of the random sins that get added
--  on top. we start out with some just basic sins, and
--  each of those 'i=0,6' loops below adds another layer
--  of slightly-denser random values
--
-- 'i=0,6' means that 6 layers of more-specific sins are
--  added on top of each other. you can see in the loop,
--  with each layer 'g' gets doubled, and 'g' is used
--  as a modifier in the sin calls.
--
-- k stores the output colour

-- ground's x input
w=x/6
-- sky's x input, includes t() because the sky moves
if(y<m) then
  w=x/2+t()*2
end
g=y/512
k=0
for i=0,6 do
  g*=2
  k+=.6^i*(sin(sin(g/2)/2-g*w/y)+sin(g))
end

-- convert k into a value that fits our palette.
-- the +7 here shifts it to the green ground colours
--  in our display palette
k=abs(k^3)+7
-- post-processing on the sky's colour value
if (y<m) then
  -- because the initial 'w' value is so much higher for
  --  the sky, we need to do the /4 on 'k' to make it
  --  usable.
  -- the k/4 shifts it halfway, and the 7/4 shifts it
  --  the other half, down so the min value is now 0.
  --  then the +1 shifts it up so the minimum value is
  --  1 (because that's where our nice blue gradient
  --  starts in the display palette).
  k=min(k/4-7/4,4)+1
end

-- make blue border at the bottom of the screen
if(y>93) then
  k=1
end

-- add the second colour for the fill pattern.
-- the \1 is a short way to do flr() (see 'Integer
--  Division') in the pico-8 manual).
-- the *16 shifts it up - the high bits of the colour
--  parameter are where the second colour lives.
k+=((k+.5)\1)*16

-- and finally, show the pixel!
pset(x,16+y,k)

-- because the screen never hits a bad-looking state,
--  there's no need to use flip here! :D
goto _