Demoscenes Collection

← Back to Home

HTML/JS Plasma Demoscene

An interactive Web Audio-enabled demoscene featuring plasma effects, particle systems, and dynamic audio visualization. Includes fullscreen toggle, audio synthesis, and real-time animation.

demoscene.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Demoscene — HTML + JS</title>
  <style>
    :root{--bg:#0b0510;--accent:#8ef;--muted:rgba(255,255,255,0.06)}
    html,body{height:100%;margin:0;background:var(--bg);color:#fff;font-family:system-ui,sans-serif}
    canvas{display:block;width:100vw;height:100vh}
    .ui{position:fixed;left:12px;top:12px;z-index:50;backdrop-filter:blur(6px);background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);padding:10px;border-radius:10px;font-size:13px}
    .ui button{background:transparent;border:1px solid rgba(255,255,255,0.06);color:inherit;padding:6px 8px;border-radius:6px;cursor:pointer}
    .ui .hint{opacity:.7;font-size:12px;margin-top:6px}
  </style>
</head>
<body>
<canvas id="c"></canvas>
<div class="ui">
  <div style="display:flex;gap:8px;align-items:center">
    <button id="toggle">Pause</button>
    <button id="audioBtn">Play Audio</button>
    <button id="fs">Fullscreen</button>
  </div>
  <div class="hint">F = fullscreen, S = toggle animation, A = toggle audio</div>
</div>

<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let W = canvas.width = innerWidth;
let H = canvas.height = innerHeight;
window.addEventListener('resize', ()=>{W = canvas.width = innerWidth;H = canvas.height = innerHeight;});

let running = true;
let t0 = performance.now();
let time = 0;

const PARTICLE_COUNT = 180;
const particles = [];
for(let i=0;i<PARTICLE_COUNT;i++){
  particles.push({x: Math.random()*W,y: Math.random()*H,vx: (Math.random()-0.5)*0.6,vy: (Math.random()-0.5)*0.6,r: Math.random()*2.6+0.6,life: Math.random()*200+100,hueOffset: Math.random()*360});
}

let audioCtx = null;
let analyser = null;
let dataArray = null;
let audioOn = false;
let source = null;

function initAudio(){
  if(audioCtx) return;
  audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  analyser = audioCtx.createAnalyser();
  analyser.fftSize = 512;
  const bufferLength = analyser.frequencyBinCount;
  dataArray = new Uint8Array(bufferLength);

  const osc1 = audioCtx.createOscillator(); osc1.type = 'sine'; osc1.frequency.value = 110;
  const osc2 = audioCtx.createOscillator(); osc2.type = 'sawtooth'; osc2.frequency.value = 220;
  const gain = audioCtx.createGain(); gain.gain.value = 0.08;
  const gain2 = audioCtx.createGain(); gain2.gain.value = 0.03;

  const noise = audioCtx.createBufferSource();
  const buffer = audioCtx.createBuffer(1, audioCtx.sampleRate * 2, audioCtx.sampleRate);
  const data = buffer.getChannelData(0);
  for(let i=0;i<data.length;i++) data[i] = (Math.random()*2-1) * 0.25;
  noise.buffer = buffer; noise.loop = true;
  const noiseGain = audioCtx.createGain(); noiseGain.gain.value = 0.02;

  osc1.connect(gain);
  osc2.connect(gain2);
  noise.connect(noiseGain);

  const out = audioCtx.createGain(); out.gain.value = 0.9;
  gain.connect(out); gain2.connect(out); noiseGain.connect(out);
  out.connect(analyser);
  analyser.connect(audioCtx.destination);

  osc1.start(); osc2.start(); noise.start();
  source = {osc1,osc2,noise,out,gain,gain2,noiseGain};
}

function toggleAudio(){
  if(!audioCtx) initAudio();
  if(!audioOn){
    if(audioCtx.state === 'suspended') audioCtx.resume();
    audioOn = true; document.getElementById('audioBtn').textContent = 'Stop Audio';
  } else {
    audioOn = false; document.getElementById('audioBtn').textContent = 'Play Audio';
    if(audioCtx) audioCtx.suspend();
  }
}

document.getElementById('audioBtn').addEventListener('click', toggleAudio);
document.getElementById('toggle').addEventListener('click', ()=>{running = !running;document.getElementById('toggle').textContent = running? 'Pause' : 'Resume';if(running) requestAnimationFrame(loop);});
document.getElementById('fs').addEventListener('click', ()=>{if(document.fullscreenElement) document.exitFullscreen(); else document.documentElement.requestFullscreen();});
window.addEventListener('keydown',(e)=>{
  if(e.key.toLowerCase()==='f'){if(document.fullscreenElement) document.exitFullscreen(); else document.documentElement.requestFullscreen();}
  if(e.key.toLowerCase()==='s'){running = !running;document.getElementById('toggle').textContent = running? 'Pause' : 'Resume';if(running) requestAnimationFrame(loop);}
  if(e.key.toLowerCase()==='a') toggleAudio();
});

function palette(a){
  const r = Math.sin(0.3*a + 0) * 0.5 + 0.5;
  const g = Math.sin(0.3*a + 2) * 0.5 + 0.5;
  const b = Math.sin(0.3*a + 4) * 0.5 + 0.5;
  return [Math.floor(r*255),Math.floor(g*255),Math.floor(b*255)];
}

function drawPlasma(ctx, w, h, t, intensity){
  const img = ctx.createImageData(w, h);
  const data = img.data;
  const cx = w/2, cy = h/2;
  const scale = 0.0045 * (1 + 0.3*Math.sin(t*0.6));
  for(let y=0;y<h;y++){
    for(let x=0;x<w;x++){
      const i = (y*w + x) * 4;
      const dx = x - cx;
      const dy = y - cy;
      const d = Math.sqrt(dx*dx + dy*dy);
      const v = Math.sin((dx*scale*80) + t*1.2) + Math.cos((dy*scale*80) - t*1.3) + Math.sin(d*scale*40 - t*0.7);
      const col = palette((v + t*0.8) * 20);
      data[i] = Math.min(255, col[0] * (0.6 + intensity));
      data[i+1] = Math.min(255, col[1] * (0.6 + intensity*0.9));
      data[i+2] = Math.min(255, col[2] * (0.6 + intensity*0.7));
      data[i+3] = 220;
    }
  }
  ctx.putImageData(img, 0, 0);
}

function drawParticles(ctx, t, intensity){
  ctx.save();
  ctx.globalCompositeOperation = 'lighter';
  for(const p of particles){
    p.x += p.vx * (1 + intensity*3);
    p.y += p.vy * (1 + intensity*3);
    p.life -= 1 + intensity*2;
    if(p.x < -20) p.x = W+20; if(p.x > W+20) p.x = -20;
    if(p.y < -20) p.y = H+20; if(p.y > H+20) p.y = -20;
    if(p.life <= 0){
      p.x = Math.random()*W; p.y = Math.random()*H; p.life = Math.random()*200+100; p.vx = (Math.random()-0.5)*0.6; p.vy = (Math.random()-0.5)*0.6;
    }
    const hue = (t*40 + p.hueOffset) % 360;
    ctx.beginPath();
    const rad = p.r * (1 + intensity*2);
    const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, rad*6);
    const c1 = `hsla(${hue},90%,60%,0.9)`;
    const c2 = `hsla(${(hue+50)%360},80%,45%,0.02)`;
    grad.addColorStop(0,c1);
    grad.addColorStop(1,c2);
    ctx.fillStyle = grad;
    ctx.arc(p.x,p.y,rad*6,0,Math.PI*2);
    ctx.fill();
  }
  ctx.restore();
}

function drawTunnel(ctx, w, h, t, intensity){
  ctx.save();
  ctx.translate(w/2,h/2);
  ctx.rotate(t*0.1);
  const layers = 40;
  for(let i=layers;i>0;i--){
    const frac = i/layers;
    const r = Math.pow(frac,0.6) * Math.min(w,h) * 0.9;
    ctx.beginPath();
    ctx.lineWidth = Math.max(1, (1-frac)*12);
    ctx.strokeStyle = `hsla(${(t*40 + i*8) % 360},80%,55%,${0.06 + (1-frac)*0.18 * (0.6+intensity)})`;
    ctx.arc(0,0,r,0,Math.PI*2);
    ctx.stroke();
  }
  ctx.restore();
}

function loop(now){
  if(!running) return;
  const dt = (now - t0) / 1000;
  t0 = now;
  time += dt;

  let intensity = 0;
  if(analyser && audioOn){
    analyser.getByteFrequencyData(dataArray);
    let sum = 0; let count = 0;
    for(let i=2;i<60;i++){ sum += dataArray[i]; count++; }
    const avg = (sum/count) / 255;
    intensity = Math.pow(avg,1.6);
  }

  ctx.fillStyle = 'rgba(6,5,8,0.18)';
  ctx.fillRect(0,0,W,H);

  const offW = Math.max(160, Math.floor(W/2));
  const offH = Math.max(120, Math.floor(H/2));
  const off = document.createElement('canvas'); off.width = offW; off.height = offH;
  const offCtx = off.getContext('2d');
  drawPlasma(offCtx, offW, offH, time*0.7, intensity);
  ctx.save();
  const sx = 1 + Math.sin(time*0.6)*0.015 * (1+intensity*2);
  const sy = 1 + Math.cos(time*0.7)*0.015 * (1+intensity*2);
  ctx.globalAlpha = 0.95;
  ctx.drawImage(off, 0, 0, offW, offH, (1-sx)/2*W, (1-sy)/2*H, W*sx, H*sy);
  ctx.restore();

  drawTunnel(ctx,W,H,time,intensity);
  drawParticles(ctx,time,intensity);

  const g = ctx.createRadialGradient(W/2,H/2,Math.min(W,H)*0.15,W/2,H/2,Math.max(W,H));
  g.addColorStop(0,'rgba(0,0,0,0)');
  g.addColorStop(1,'rgba(0,0,0,0.5)');
  ctx.fillStyle = g; ctx.fillRect(0,0,W,H);

  ctx.fillStyle = 'rgba(255,255,255,0.06)';
  ctx.fillRect(12,H-60,220,44);
  ctx.fillStyle = 'rgba(255,255,255,0.65)';
  ctx.font = '12px system-ui,Segoe UI,Roboto';
  ctx.fillText('Demoscene — HTML + JS', 22, H-36);
  ctx.fillStyle = 'rgba(255,255,255,0.45)';
  ctx.fillText('Press F fullscreen  •  A toggle audio  •  S pause', 22, H-20);

  requestAnimationFrame(loop);
}

requestAnimationFrame((ts)=>{ t0 = ts; loop(ts); });
window._demoscene = {toggleAudio, start: ()=>{running=true; requestAnimationFrame(loop)}, stop: ()=>{running=false}};
</script>
</body>
</html>

Usage Instructions

  1. Copy the entire code above
  2. Save as an HTML file (e.g., demoscene.html)
  3. Open in any modern browser (Chrome, Firefox, Edge recommended)
  4. Controls:
    • F - Toggle fullscreen
    • A - Toggle audio synthesis
    • S - Pause/resume animation
    • Mouse - UI buttons for controls

Tkinter Sinusoids Demoscene

A Python/Tkinter demoscene featuring animated sinusoidal waves with dynamic colors, particle effects, and interactive controls for speed adjustment and fullscreen toggling.

demoscene_tk.py

import tkinter as tk
import time
import math
import random

def hsv_to_hex(h,s,v):
    h_i = int(h*6)
    f = h*6 - h_i
    p = v*(1-s)
    q = v*(1-f*s)
    t = v*(1-(1-f)*s)
    r,g,b = [(v,t,p),(q,v,p),(p,v,t),(p,q,v),(t,p,v),(v,p,q)][h_i%6]
    return '#%02x%02x%02x' % (int(r*255),int(g*255),int(b*255))

class Demoscene:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Tkinter Demoscene — Sinusoids")
        self.fullscreen = False
        self.w = 1200
        self.h = 700
        self.canvas = tk.Canvas(self.root, width=self.w, height=self.h, bg='#07030a', highlightthickness=0)
        self.canvas.pack(fill=tk.BOTH, expand=True)
        self.root.bind('<Configure>', self.on_resize)
        self.root.bind('<space>', self.toggle)
        self.root.bind('f', self.toggle_full)
        self.root.bind('F', self.toggle_full)
        self.root.bind('<Up>', self.speed_up)
        self.root.bind('<Down>', self.speed_down)
        self.paused = False
        self.start = time.perf_counter()
        self.speed = 1.0
        self.layers = []
        self.layer_count = 8
        for i in range(self.layer_count):
            hue = random.random()
            color = hsv_to_hex(hue,0.8,0.9)
            amplitude = (8 + i*6)
            freq = 0.8 + i*0.15
            phase = random.random()*math.pi*2
            thickness = 1 + (i%3)
            self.layers.append({'hue':hue,'color':color,'amp':amplitude,'freq':freq,'phase':phase,'thick':thickness})
        self.trail = []
        self.max_trail = 12
        self.animate()

    def on_resize(self,event):
        if event.width>10 and event.height>10:
            self.w = event.width
            self.h = event.height
            self.canvas.config(width=self.w, height=self.h)

    def toggle(self,event=None):
        self.paused = not self.paused
        if not self.paused:
            self.start = time.perf_counter() - self.t_offset

    def toggle_full(self,event=None):
        self.fullscreen = not self.fullscreen
        self.root.attributes('-fullscreen', self.fullscreen)

    def speed_up(self,event=None):
        self.speed *= 1.15

    def speed_down(self,event=None):
        self.speed /= 1.15

    def draw_wave(self,layer,t):
        pts = []
        amp = layer['amp'] * (1 + 0.35*math.sin(t*0.9 + layer['phase']))
        freq = layer['freq']
        phase = t*freq*1.6 + layer['phase']
        mid = self.h/2
        step = max(2, int(self.w/600))
        for x in range(0,self.w+1,step):
            nx = x/self.w*math.pi*2
            y = mid + math.sin(nx* (1.6+freq) + phase + x*0.003)*amp + math.sin((x*0.01)+t*1.2+layer['phase'])* (amp*0.35)
            pts.append(x); pts.append(y)
        id = self.canvas.create_line(*pts, fill=layer['color'], width=layer['thick'], smooth=True, splinesteps=8)
        return id

    def fade_background(self):
        self.canvas.create_rectangle(0,0,self.w,self.h, fill='#07030a', outline='')

    def animate(self):
        now = time.perf_counter()
        if self.paused:
            if not hasattr(self,'t_offset'):
                self.t_offset = now - self.start
            self.root.after(30, self.animate)
            return
        self.t_offset = 0
        t = (now - self.start) * self.speed
        self.canvas.delete('all')
        glow_count = 6
        for i in range(glow_count):
            alpha = int(8 + i*6)
            shade = '#%02x%02x%02x' % (alpha, int(alpha*0.7), int(alpha*0.4))
            self.canvas.create_oval(-self.w*0.6+i*40, -self.h*0.6+i*30, self.w*1.6-i*40, self.h*1.6-i*30, fill=shade, outline='')
        for idx,layer in enumerate(self.layers):
            layer_id = self.draw_wave(layer, t + idx*0.13)
            self.canvas.itemconfigure(layer_id, stipple='')
        particle_count = 90
        for i in range(particle_count):
            x = (i/particle_count)*self.w + math.sin(t*0.9 + i)*12
            y = self.h*0.5 + math.cos((i*0.2)+t*1.6)* (30 + math.sin(i*0.4+t*0.7)*40)
            r = 1 + (math.sin(t*2 + i)*0.6 + 0.6)*2
            hue = (0.6 + math.sin(i*0.1 + t*0.4)*0.2) % 1.0
            color = hsv_to_hex(hue,0.7,0.95)
            self.canvas.create_oval(x-r,y-r,x+r,y+r, fill=color, outline='')
        self.canvas.create_rectangle(12,self.h-62,260,self.h-12, fill='black', outline='', stipple='')
        self.canvas.create_text(22,self.h-46, anchor='w', text='Tkinter Demoscene — Sinusoids', fill='#e7eefc', font=('Helvetica',12,'bold'))
        self.canvas.create_text(22,self.h-28, anchor='w', text='Space pause/resume  F fullscreen  Up/Down speed', fill='#cfe0ff', font=('Helvetica',10))
        self.root.after(16, self.animate)

if __name__ == '__main__':
    Demoscene()
    tk.mainloop()

Usage Instructions

  1. Save the code as demoscene_tk.py
  2. Run with Python 3: python demoscene_tk.py
  3. Requirements: Python 3 with Tkinter (usually included)
  4. Controls:
    • Space - Pause/resume animation
    • F - Toggle fullscreen
    • Up Arrow - Increase animation speed
    • Down Arrow - Decrease animation speed

Particle Attractor Demoscene

Interactive particle system where particles are attracted to mouse cursor. Features dynamic colors, smooth particle physics, and real-time mouse interaction.

demoscene_particles.py

import tkinter as tk, time, math, random

def hsv(h,s,v):
    i=int(h*6);f=h*6-i;p=v*(1-s);q=v*(1-f*s);t=v*(1-(1-f)*s)
    r,g,b=[(v,t,p),(q,v,p),(p,v,t),(p,q,v),(t,p,v),(v,p,q)][i%6]
    return '#%02x%02x%02x'%(int(r*255),int(g*255),int(b*255))

class P:
    def __init__(self):
        self.root=tk.Tk();self.root.title('Particle Attractor');self.w=1100;self.h=700
        self.c=tk.Canvas(self.root,width=self.w,height=self.h,bg='#030113',highlightthickness=0);self.c.pack(fill=tk.BOTH,expand=True)
        self.root.bind('<Configure>',self.resize);self.root.bind('<space>',self.toggle);self.root.bind('f',self.full)
        self.particles=[{'x':random.random()*self.w,'y':random.random()*self.h,'vx':0,'vy':0,'h':random.random()} for _ in range(220)]
        self.mx=self.w/2;self.my=self.h/2;self.root.bind('<Motion>',lambda e: [setattr(self,'mx',e.x),setattr(self,'my',e.y)])
        self.start=time.perf_counter();self.paused=False;self.loop()
    
    def resize(self,e):self.w=e.width;self.h=e.height;self.c.config(width=self.w,height=self.h)
    
    def toggle(self,e=None):self.paused=not self.paused
    
    def full(self,e=None):self.root.attributes('-fullscreen', not self.root.attributes('-fullscreen'))
    
    def loop(self):
        now=time.perf_counter()
        if not self.paused:
            self.c.delete('all')
            for p in self.particles:
                dx=self.mx-p['x'];dy=self.my-p['y'];d=math.hypot(dx,dy)+1
                fx=dx/d**1.3;fy=dy/d**1.3
                p['vx']=p['vx']*0.92+fx*2; p['vy']=p['vy']*0.92+fy*2
                p['x']+=p['vx'];p['y']+=p['vy']
                if p['x']<0: p['x']=self.w
                if p['x']>self.w: p['x']=0
                if p['y']<0: p['y']=self.h
                if p['y']>self.h: p['y']=0
                r=1.2+abs(p['vx'])*1.3
                col=hsv((p['h']+0.2*math.sin(now+p['x']*0.01))%1,0.8,0.95)
                self.c.create_oval(p['x']-r,p['y']-r,p['x']+r,p['y']+r,fill=col,outline='')
            self.c.create_text(18,self.h-40,anchor='w',text='Particle Attractor — move mouse to attract • Space pause • F fullscreen',fill='#dfe',font=('Helvetica',12))
        self.root.after(16,self.loop)

if __name__=='__main__':
    P();tk.mainloop()

Usage Instructions

  1. Save as demoscene_particles.py
  2. Run with: python demoscene_particles.py
  3. Move your mouse to attract particles
  4. Controls:
    • Mouse movement - Attract particles to cursor
    • Space - Pause/resume animation
    • F - Toggle fullscreen

Cartesian Demo

Simple Cartesian coordinate system demo featuring animated waves, rotating points, and line animations with colorful visual effects.

CartesianDemo.py

import tkinter as tk
import math
import random

root = tk.Tk()
root.title("Cartesian Demoscene Show")
canvas = tk.Canvas(root, width=800, height=500, bg="black")
canvas.pack()

width, height = 800, 500
center_x, center_y = width//2, height//2

wave_points = []
wave_x = 0
wave_amplitude = 80
wave_frequency = 0.03

angle = 0
rot_radius = 120

line_x = 0

colors = ["cyan", "magenta", "lime", "yellow", "orange"]

canvas.create_line(0, center_y, width, center_y, fill="white")
canvas.create_line(center_x, 0, center_x, height, fill="white")

def animate():
    global wave_x, wave_points, angle, line_x
    
    canvas.delete("wave")
    canvas.delete("point")
    canvas.delete("line")
    
    y_wave = center_y + wave_amplitude * math.sin(wave_frequency * wave_x)
    wave_points.append((wave_x, y_wave))
    for i in range(1, len(wave_points)):
        canvas.create_line(wave_points[i-1], wave_points[i],
                           fill=random.choice(colors), width=2, tags="wave")
    wave_x += 3
    if wave_x > width:
        wave_x = 0
        wave_points = []
    
    x_rot = center_x + rot_radius * math.cos(angle)
    y_rot = center_y + rot_radius * math.sin(angle)
    canvas.create_oval(x_rot-5, y_rot-5, x_rot+5, y_rot+5, fill="cyan", tags="point")
    angle += 0.06
    
    y_line = center_y + 100 * math.sin(line_x/40)
    canvas.create_line(center_x, center_y, center_x + line_x, y_line, fill="magenta", width=3, tags="line")
    line_x += 3
    if line_x > width//2:
        line_x = 0
    
    root.after(30, animate)

animate()
root.mainloop()

Usage Instructions

  1. Save as CartesianDemo.py
  2. Run with: python CartesianDemo.py
  3. Features:
    • Animated sine wave across Cartesian plane
    • Rotating point in circular motion
    • Dynamic line animation from center
    • Random color selection for wave segments

Lissajous Aurora Demoscene

Beautiful Lissajous curve visualization with aurora-like effects, animated particles, and smooth color transitions.

demoscene_lissajous.py

import tkinter as tk, time, math, random

def hsv(h,s,v):
    i=int(h*6);f=h*6-i;p=v*(1-s);q=v*(1-f*s);t=v*(1-(1-f)*s)
    r,g,b=[(v,t,p),(q,v,p),(p,v,t),(p,q,v),(t,p,v),(v,p,q)][i%6]
    return '#%02x%02x%02x'%(int(r*255),int(g*255),int(b*255))

class D:
    def __init__(self):
        self.root=tk.Tk();self.root.title('Lissajous Aurora');self.w=1100;self.h=700
        self.c=tk.Canvas(self.root,width=self.w,height=self.h,bg='#020113',highlightthickness=0);self.c.pack(fill=tk.BOTH,expand=True)
        self.root.bind('<Configure>',self.resize);self.root.bind('<space>',self.toggle);self.root.bind('f',self.full)
        self.start=time.perf_counter();self.paused=False;self.speed=1.0;self.a=3;self.b=2;self.ang=0
        self.layers=[(random.random(),20+ i*10,1+0.2*i) for i in range(9)]
        self.loop()
    
    def resize(self,e):self.w=e.width;self.h=e.height;self.c.config(width=self.w,height=self.h)
    
    def toggle(self,e=None):self.paused=not self.paused
    
    def full(self,e=None):self.root.attributes('-fullscreen', not self.root.attributes('-fullscreen'))
    
    def loop(self):
        now=time.perf_counter()
        if not self.paused:
            t=(now-self.start)*self.speed;self.ang=t*0.8
            self.c.delete('all')
            for i,(hue,amp,thick) in enumerate(self.layers):
                pts=[]
                A=amp*(1+0.3*math.sin(t*0.6+i));B=amp*(1+0.2*math.cos(t*0.9+i))
                for x in range(0,self.w,8):
                    nx=x/self.w*math.pi*2
                    y=self.h/2+math.sin(self.a*nx+self.ang*(0.6+i*0.02))*A+math.cos(self.b*nx+self.ang*(0.9+i*0.03))*B
                    pts.append(x);pts.append(y)
                col=hsv((hue+0.1*math.sin(t*0.3))%1,0.8,0.95)
                self.c.create_line(*pts,fill=col,width=thick,smooth=True,splinesteps=8)
            for p in range(120):
                x=(p/self.w)*self.w+math.sin(t*0.9+p)*12
                y=self.h*0.5+math.cos(p*0.2+t*1.6)*(30+math.sin(p*0.4+t*0.7)*40)
                r=1+ (math.sin(t*2+p)*0.6+0.6)*2
                col=hsv((0.6+math.sin(p*0.12+t*0.3)*0.2)%1,0.7,0.95)
                self.c.create_oval(x-r,y-r,x+r,y+r,fill=col,outline='')
            self.c.create_text(18,self.h-40,anchor='w',text='Lissajous Aurora  Space pause  F fullscreen',fill='#dfe',font=('Helvetica',12))
        self.root.after(16,self.loop)

if __name__=='__main__':
    D();tk.mainloop()

Usage Instructions

  1. Save as demoscene_lissajous.py
  2. Run with: python demoscene_lissajous.py
  3. Features:
    • Lissajous curves with multiple layers
    • Dynamic particle effects
    • HSV color transitions
    • Smooth animation with adjustable speed
  4. Controls:
    • Space - Pause/resume animation
    • F - Toggle fullscreen

Conway's Game of Life

Interactive Conway's Game of Life simulation with full controls. Features pattern presets, adjustable speed, and real-time statistics.

game_of_life.py

import tkinter as tk
import random
import time

class GameOfLife:
    def __init__(self, root):
        self.root = root
        self.root.title("Conway's Game of Life")
        self.root.configure(bg='#0f172a')
        
        # Game configuration
        self.rows = 40
        self.cols = 60
        self.cell_size = 15
        self.speed = 10  # generations per second
        self.is_running = False
        self.generation = 0
        
        # Initialize grid
        self.grid = [[0 for _ in range(self.cols)] for _ in range(self.rows)]
        
        # Colors
        self.colors = {
            'bg': '#0f172a',
            'grid': '#1e293b',
            'cell': '#10b981',
            'text': '#e2e8f0',
            'button': '#3b82f6',
            'button_hover': '#2563eb'
        }
        
        self.setup_ui()
        self.setup_bindings()
        
    def setup_ui(self):
        # Control frame
        control_frame = tk.Frame(self.root, bg=self.colors['bg'])
        control_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
        
        # Buttons
        buttons = [
            ("Start", self.start_game, "#10b981"),
            ("Pause", self.pause_game, "#f59e0b"),
            ("Reset", self.reset_game, "#ef4444"),
            ("Clear", self.clear_grid, "#6b7280"),
            ("Random", self.random_grid, "#8b5cf6"),
            ("Step", self.step, "#3b82f6"),
        ]
        
        for text, command, color in buttons:
            btn = tk.Button(
                control_frame, 
                text=text, 
                command=command,
                bg=color,
                fg='white',
                font=('Arial', 10, 'bold'),
                relief=tk.FLAT,
                padx=15,
                pady=5,
                cursor='hand2'
            )
            btn.pack(side=tk.LEFT, padx=5)
        
        # Speed control
        speed_frame = tk.Frame(control_frame, bg=self.colors['bg'])
        speed_frame.pack(side=tk.LEFT, padx=20)
        
        tk.Label(speed_frame, text="Speed:", bg=self.colors['bg'], fg=self.colors['text'], 
                font=('Arial', 10)).pack(side=tk.LEFT, padx=(0, 5))
        
        self.speed_slider = tk.Scale(
            speed_frame,
            from_=1,
            to=20,
            orient=tk.HORIZONTAL,
            length=100,
            bg=self.colors['bg'],
            fg=self.colors['text'],
            highlightthickness=0,
            command=self.change_speed
        )
        self.speed_slider.set(self.speed)
        self.speed_slider.pack(side=tk.LEFT)
        
        # Stats frame
        stats_frame = tk.Frame(self.root, bg=self.colors['bg'])
        stats_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=(0, 10))
        
        self.stats_labels = {}
        stats = [
            ("Generation:", "generation", "0"),
            ("Live Cells:", "live_cells", "0"),
            ("Status:", "status", "Paused")
        ]
        
        for label, key, value in stats:
            frame = tk.Frame(stats_frame, bg=self.colors['bg'])
            frame.pack(side=tk.LEFT, padx=20)
            
            tk.Label(frame, text=label, bg=self.colors['bg'], fg=self.colors['text'], 
                    font=('Arial', 10)).pack(side=tk.LEFT)
            
            self.stats_labels[key] = tk.Label(
                frame, 
                text=value, 
                bg=self.colors['bg'], 
                fg=self.colors['cell'],
                font=('Arial', 10, 'bold'),
                width=10
            )
            self.stats_labels[key].pack(side=tk.LEFT, padx=(5, 0))
        
        # Canvas for grid
        self.canvas = tk.Canvas(
            self.root,
            width=self.cols * self.cell_size,
            height=self.rows * self.cell_size,
            bg=self.colors['grid'],
            highlightthickness=0
        )
        self.canvas.pack(padx=10, pady=(0, 10))
        self.draw_grid()
        
        # Pattern buttons
        pattern_frame = tk.Frame(self.root, bg=self.colors['bg'])
        pattern_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=10)
        
        patterns = [
            ("Glider", self.add_glider),
            ("Blinker", self.add_blinker),
            ("Glider Gun", self.add_glider_gun),
            ("Random Fill", self.random_grid)
        ]
        
        for text, command in patterns:
            btn = tk.Button(
                pattern_frame,
                text=text,
                command=command,
                bg='#475569',
                fg='white',
                font=('Arial', 9),
                relief=tk.FLAT,
                padx=10,
                pady=3,
                cursor='hand2'
            )
            btn.pack(side=tk.LEFT, padx=5)
    
    def setup_bindings(self):
        self.canvas.bind('', self.toggle_cell)
        self.root.bind('', lambda e: self.toggle_running())
        self.root.bind('r', lambda e: self.reset_game())
        self.root.bind('c', lambda e: self.clear_grid())
        self.root.bind('s', lambda e: self.step())
    
    def draw_grid(self):
        self.canvas.delete('all')
        
        # Draw cells
        for row in range(self.rows):
            for col in range(self.cols):
                x1 = col * self.cell_size
                y1 = row * self.cell_size
                x2 = x1 + self.cell_size
                y2 = y1 + self.cell_size
                
                if self.grid[row][col]:
                    self.canvas.create_rectangle(
                        x1, y1, x2, y2,
                        fill=self.colors['cell'],
                        outline=self.colors['grid'],
                        width=1
                    )
                else:
                    self.canvas.create_rectangle(
                        x1, y1, x2, y2,
                        fill=self.colors['bg'],
                        outline=self.colors['grid'],
                        width=1
                    )
    
    def toggle_cell(self, event):
        if self.is_running:
            return
        
        col = event.x // self.cell_size
        row = event.y // self.cell_size
        
        if 0 <= row < self.rows and 0 <= col < self.cols:
            self.grid[row][col] = 1 - self.grid[row][col]
            self.draw_grid()
            self.update_stats()
    
    def count_neighbors(self, row, col):
        count = 0
        for i in range(-1, 2):
            for j in range(-1, 2):
                if i == 0 and j == 0:
                    continue
                r = (row + i) % self.rows
                c = (col + j) % self.cols
                count += self.grid[r][c]
        return count
    
    def next_generation(self):
        new_grid = [[0 for _ in range(self.cols)] for _ in range(self.rows)]
        
        for row in range(self.rows):
            for col in range(self.cols):
                neighbors = self.count_neighbors(row, col)
                
                if self.grid[row][col]:
                    # Cell is alive
                    if neighbors in [2, 3]:
                        new_grid[row][col] = 1
                else:
                    # Cell is dead
                    if neighbors == 3:
                        new_grid[row][col] = 1
        
        self.grid = new_grid
        self.generation += 1
        self.draw_grid()
        self.update_stats()
        
        # Stop if no live cells
        if self.count_live_cells() == 0 and self.is_running:
            self.pause_game()
    
    def count_live_cells(self):
        return sum(sum(row) for row in self.grid)
    
    def update_stats(self):
        self.stats_labels['generation'].config(text=str(self.generation))
        self.stats_labels['live_cells'].config(text=str(self.count_live_cells()))
        self.stats_labels['status'].config(
            text="Running" if self.is_running else "Paused",
            fg="#10b981" if self.is_running else "#f59e0b"
        )
    
    def start_game(self):
        if not self.is_running:
            self.is_running = True
            self.update_stats()
            self.run_game()
    
    def pause_game(self):
        self.is_running = False
        self.update_stats()
    
    def toggle_running(self):
        if self.is_running:
            self.pause_game()
        else:
            self.start_game()
    
    def run_game(self):
        if self.is_running:
            self.next_generation()
            delay = 1000 // self.speed
            self.root.after(delay, self.run_game)
    
    def reset_game(self):
        self.pause_game()
        self.grid = [[0 for _ in range(self.cols)] for _ in range(self.rows)]
        self.generation = 0
        self.draw_grid()
        self.update_stats()
    
    def clear_grid(self):
        self.reset_game()
    
    def random_grid(self):
        self.pause_game()
        for row in range(self.rows):
            for col in range(self.cols):
                self.grid[row][col] = 1 if random.random() < 0.2 else 0
        self.generation = 0
        self.draw_grid()
        self.update_stats()
    
    def step(self):
        self.pause_game()
        self.next_generation()
    
    def change_speed(self, value):
        self.speed = int(value)
    
    def add_glider(self):
        self.pause_game()
        center_row = self.rows // 2
        center_col = self.cols // 2
        
        # Glider pattern
        pattern = [(0, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
        
        for dr, dc in pattern:
            row = (center_row + dr) % self.rows
            col = (center_col + dc) % self.cols
            self.grid[row][col] = 1
        
        self.draw_grid()
        self.update_stats()
    
    def add_blinker(self):
        self.pause_game()
        center_row = self.rows // 2
        center_col = self.cols // 2
        
        # Blinker pattern
        for dc in range(-1, 2):
            row = center_row
            col = (center_col + dc) % self.cols
            self.grid[row][col] = 1
        
        self.draw_grid()
        self.update_stats()
    
    def add_glider_gun(self):
        self.pause_game()
        center_row = self.rows // 2
        center_col = self.cols // 2
        
        # Gosper Glider Gun pattern (simplified)
        gun_pattern = [
            (5, 1), (5, 2), (6, 1), (6, 2),
            (5, 11), (6, 11), (7, 11),
            (4, 12), (8, 12),
            (3, 13), (9, 13),
            (3, 14), (9, 14),
            (6, 15),
            (4, 16), (8, 16),
            (5, 17), (6, 17), (7, 17),
            (6, 18)
        ]
        
        for dr, dc in gun_pattern:
            row = (center_row + dr) % self.rows
            col = (center_col + dc) % self.cols
            if 0 <= row < self.rows and 0 <= col < self.cols:
                self.grid[row][col] = 1
        
        self.draw_grid()
        self.update_stats()

if __name__ == "__main__":
    root = tk.Tk()
    game = GameOfLife(root)
    root.mainloop()

Usage Instructions

  1. Save as game_of_life.py
  2. Run with: python game_of_life.py
  3. Requirements: Python 3 with Tkinter
  4. Controls:
    • Click cells - Toggle alive/dead (when paused)
    • Space - Start/pause simulation
    • R - Reset grid
    • C - Clear grid
    • S - Step forward one generation
    • Buttons - Start, Pause, Reset, Clear, Random, Step
    • Slider - Adjust simulation speed
    • Pattern buttons - Add predefined patterns
  5. Features:
    • Interactive grid with click-to-toggle cells
    • Real-time generation and cell count statistics
    • Adjustable simulation speed
    • Pattern presets (Glider, Blinker, Glider Gun)
    • Random grid generation
    • Automatic pause when no live cells remain