And that "one single place" is for this trivial toy program. Now imagine the impedance across the delivery of a real business app.
That seemed very incongruous with my experience. I think the problem is that in my experience the number of types necessary scales sub-linearly with the amount of code. This is because Haskell (and other languages with what one might consider pleasant type systems) generally have full type inference. So while you might need the explicit type for the toy example, scaling that example up means only a handful, and possibly a net negative, other necessary explicit types. Prelude> let add = (+) <$> readLn <*> readLn
Prelude> let foo = (/) <$> add <*> add
Prelude> foo
3
30.1
40
10.2
0.6593625498007968
So it's quite possible that the "type burden" becomes smaller as your system grows.
The basic idea was to use a chain of Viewports and TextureRects using the previous ViewportTexture to do the effect. This is essentially just setting up a chain of framebuffers that I can draw on top of at each step. The first step just does a simple calculation to convert the incoming color to an "energy" value with a simple shader on the whole frame. Then there are two decay viewports that feed each other to decay the old frame and overlay the new one. These just have a decay parameter that I can tweak to get different effects. There's a final Viewport that supersamples everything because I'm going for more of a vector display look than a CRT. And I can layer on other effects at either the energy state (like decaying phoshpor) or at the final stage (like flicker or screen curve).
Here I've exaggerated the decay quite a bit so you can see the effect. https://www.youtube.com/watch?v=2ZrvcZIfqOI The trails there are entirely from this effect. I'm only rendering the spinning figure. You can also see where lines overlap they are brighter than the surrounding lines because I cap the maximum energy value in the first layer.