[WIP] SkyBerron Dot Tunnel

Dot tunnet effect, reusing code from my previous fill rectangle tunnel effect.

If someone wants to know how does it work, take a look at this: https://www.lexaloffle.com/bbs/?pid=44036

Btw, if you are interested in old-school demo effects, googling around you will find there are quite a few web pages that explain some nice effects:

SkyBerron Dot Tunnel.bin.7

SkyBerron Dot Tunnel.bin.2

SkyBerron Dot Tunnel.bin (45.9 KB)

With some background colors:

SkyBerron Dot Tunnel.bin.1

SkyBerron Dot Tunnel.bin.2

SkyBerron Dot Tunnel.bin (45.9 KB)


@SkyBerron , not gonna lie. These are really nice demos. But what’s in it for the community, apart from admiring your skills? I look at it from the perspective of game disk, for example. These all could be featured in a Demos folder. But since there is no further information, what would be the point?

To me, the ancient art of demoscene should be shared and taught. Its only on a platform like Pokitto that the tips and tricks have any real significance any more. On PCs and mobiles with gpus and fpus they are irrelevant.

We have a living, breathing platform in Pokitto. Its not a zombie like C64 or Amiga, that exists only by reanimating or emulating a commercial tombstone.

Your decision to only show the end result of your demos is your choice and I have no quarrel with that. But I ask you to think of ways of sharing the knowledge you dig up for the benefit of anyone who gets the spark from looking at these nice demos.


I feel I have to reply just to be polite. But seriously, I haven’t got an answer for your question. I love problem solving and working with limited resources. I’m learning how to code C++ and gfx and I’m developing my skills in the MCU world. Also part of my motivation is showing off what can be done with this ‘poor’ Cortex M0+, a ‘microwave grade’ MCU. I hope my work inspires others to go further. My coding is evolving and getting better, but the quality is still so low I would be ashamed of sharing, and tbh, I think there are much better source code out there to learn from. As much I like coding as a hobby, I hate polishing and maintaining it, once it’s working I usually lose interest. I really don’t want my programs to be included in any gamedisks or serve as promo material. I just code things and have fun. Hope you understand.


I understand and I appreciate a straight answer. Still, I feel there would be much to be shared in the knowledge you are digging up.

We will leave things as they are. I just wanted to share my feelings on this with you.

1 Like

Wow … I can see this as the background for a 3D space invaders sort of game where the aliens approach you from over the horizon.

@SkyBerron is the maximum FPS it can do? Would trying to render some aliens over the top kill the performance?


This is exactly what I was driving at. Some of these demos could be the base for some really cool game ideas.

1 Like

You know, I’m really liking these demos. It like I’m loading up example BASIC listings from an 80’s graphics programming book.

It would be really nice to see all of the source code however. Even if you don’t consider it good enough for public viewing. My code is always a complete mess, unsorted with completely unused functions and redundant maths, quite often with parts that are completely wrong. But I come from the mindset that if it does what you set out to do, then it IS good enough, even if some other people think it’s formatted wrong or whatever you might not be happy about. I learned to code in BASIC and my brain got stuck there, my code quite often reflects that.
So regardless of how ‘bad’ your code is, we might all learn something from it.

But either way, keep up the good work, the demos are nice :sunglasses::+1:

[Edit] in fact, why not use 1 or 2 of these demos as a short article for the next issue of pokitto magazine? Just briefly explain the tricks used to get good speed buffer less display working?:stuck_out_tongue_winking_eye:


Thank you very much, @filmote and @spinal. I’m now working (WFH), so I cannot give now the information you demand. I’ll reply in detail later.
My list of pending old-school effects is still very long and I’m not sure I will be able to get some of them working under strong memory constraints. But if you want some specific graphic effect, just propose it and point out a demo where it was shown (if possible, also a Youtube video).


@filmote: you can get pretty good framerates through finetuning number of ellipses and number of dots per ellipse. Anyway, I would not recommend using this effect as background for gameplay as ppl could feel dizzy after a few minutes playing. But I guess it’s perfectly fine for menus or intro screens.

The keys for speed are:

  • Floats and trigonometric functions are not allowed in inner loop (that’s the critical section of code for speed).
  • Only fixed point math and look up tables are allowed in inner loop.
  • You need a pretty fast setpixel() function (I’m using an indexed array write :slight_smile: )
  • Choose carefully number of ellipses/rings being drawn, number of dots per ellipse, distance between rings.

@spinal: I try to make things both simple and nice, although I usually spend less time fine tuning than I ought to (target: reach “hey, it works” status asap, then move on). Usually, sheets of code using raw fixed point math and look up tables tend to look ugly and I usually don’t waste my time beautifying them.

Ok, the most important part of code looks like this. I am using my own SBDL for graphics at the moment, so you would need to change drawing primitives according to API being used. Please note that what I’m doing here is an exception, as it takes time and keeps me from coding new things. The list of gfx effects I want to code is long… and I’d like to switch to game programming at some point.

// Data structure to hold rectangle information
// Ellipses are inscribed in rectangles

struct Rect {
    int x1, y1;
    int x2, y2;

// Initialization

// Tunnel data (fixed point 16.16)
int m_nrect = 32;               // Number of rings: should be constexpr?
int m_stunnel = ( 20 << 16 );   // Distance between rings
int m_zpos = 0;                 // Current camera view position
int m_vzpos = ( 5 << 16 );      // Speed of camera
int m_zeye = ( 100 << 16 );     // Focal distance for perspective calculations

int m_xc = ( SCREEN_WIDTH >> 1 );   // Mid point of screen for centering objects
int m_yc = ( SCREEN_HEIGHT >> 1 );

Rect *m_trect = new Rect[m_nrect];	// Rect array

// Sin() look up table, also used for cos()
int m_nsin = 1 << 5; //32       // Size of sin() table equals number of dots drawn per ellipse (must be power of 2)
int m_masksin = m_nsin - 1;		// Mask with this value to avoid out of bounds reads

int *m_tsin = new int[m_nsin];

for( int i = 0; i < m_nsin; i++ ) {
	m_tsin[i] = (int) ( .5f + (1<<16) * sin( ( ( 2.0f * M_PI ) * i ) / m_nsin ) );


// Update camera position

m_zpos += m_vzpos;


// Frame setup (can use floats and trigonometric functions)

// 3d Rings
int dmin = 1 << 15;     // Used in perspective scaling to avoid divide by zero, needs fine tuning
int w0 = SCREEN_WIDTH;  // Base Rect Width
int h0 = SCREEN_HEIGHT; // Base Rect Height
int xc0 = ( SCREEN_WIDTH >> 1 );    // Center of screen
int yc0 = ( SCREEN_HEIGHT >> 1 );
int o = m_stunnel - ( m_zpos % m_stunnel ); // Distance to first ring
int d = o;       // Distance for perspective projection calculations
int z = m_zpos + o;     // Distance for oscillation calculations
float wx = .005f;       // Angular velocity X oscillation
float wy = .0057f;      // Angular velocity Y oscillation
float ax = 40.0f;       // Amplitude X oscillation
float ay = 40.0f;       // Amplitude Y oscillation
m_xc = xc0 - (int) ( ax * sin( wx * ( m_zpos >> 16 ) ) );   // Set camera position
m_yc = yc0 - (int) ( ay * sin( wy * ( m_zpos >> 16 ) ) );

for( int irect = 0; irect < m_nrect; irect++ ) {    // From nearest ring to farthest ring
	Rect &r = m_trect[irect];
	int k = m_zeye / ( max( dmin, d ) >> 16 ); // Perspective scaling coefficient
	int w = ( k * w0 ) >> 16;   // Scale axis of ellipse acording to distance to camera
	int h = ( k * h0 ) >> 16;
	int dx = (int) ( ax * sin( wx * ( z >> 16 ) ) ); // Ellipse center displacement: should apply scaling coefficient?
	int dy = (int) ( ay * sin( wy * ( z >> 16 ) ) );
	int x = m_xc + dx;
	int y = m_yc + dy;
	r.x1 = x - ( w >> 1 );  // Save rect min & max coords
	r.y1 = y - ( h >> 1 );
	r.x2 = r.x1 + w;
	r.y2 = r.y1 + h;
	d += m_stunnel; // Update distances for next ring
	z += m_stunnel;


// Clear screen (if needed)
// cls();

// Draw ellipses made of dots


for( int irect = m_nrect - 1; irect >= 0; irect-- ) {   // Draw ellipses from back to front
	Rect &r = m_trect[irect];
	int xx = r.x1 + r.x2;
	int yy = r.y1 + r.y2;
	int ww = r.x2 - r.x1 + 1;
	int hh = r.y2 - r.y1 + 1;

	// Inner loop (can't use floats nor trigonometric functions here)

	int ipt2 = ( m_nsin >> 2 ); // 90 degree phase between sin() and cos()
	for( int ipt1 = 0; ipt1 < m_nsin; ipt1++ ) {    // Number of dots: size of sin() look up table
		// Fixed point math 16.16
		int x = ( xx + ( ( ww * m_tsin[ ipt2 & m_masksin ] ) >> 16 ) ) >> 1;    // cos() from sin() lookup: sin( angle + 90deg )
		int y = ( yy + ( ( hh * m_tsin[ ipt1 ] ) >> 16 ) ) >> 1;
		if( y >= 0 && y < SCREEN_HEIGHT && x >= 0 && x < SCREEN_WIDTH ) { // Bounds checking, only needed if setpixel() doesn't do bounds checking
			setpixel( x, y, clr ); // Draw dot

Edit: fixed some bugs


Awesome … I will have a look at it!