Your first 30 minutes
From an empty tab to a night full of fireflies that react to the mouse, make sound and turn into a video. Six steps, each a complete sketch you can run: open the IDE, press Test in Dana (or paste the code into a new tab and press Run), then try the small change suggested under it. No C# knowledge needed; it comes along the way.
On this page · 6 entries
1. A canvas and a circle 3 min
Every sketch is a class that inherits from Sketch. Setup() runs once: give the canvas a size. Draw() runs about 60 times a second: clear the background and draw. MouseX and MouseY are always where the mouse is, so the circle follows it.
public class Fireflies : Sketch { public override void Setup() { Size(800, 500); } public override void Draw() { Background(15, 20, 40); Fill(255, 230, 120); NoStroke(); Circle(MouseX, MouseY, 20); } }
Background(40, 10, 30), or make the circle bigger when it's further right: Circle(MouseX, MouseY, MouseX / 10).2. Shapes and color: a night scene 5 min
Drawing is done in order, like painting: what you draw later covers what came before. Fill() and Stroke() set the colors for everything drawn after them, and a fourth number is transparency (0 to 255). Moving the scene into its own method keeps Draw() readable. RandomSeed(7) makes Random() return the same numbers every frame, so the grass doesn't flicker.
public class Fireflies : Sketch { public override void Setup() { Size(800, 500); } public override void Draw() { Background(15, 20, 40); DrawScene(); Fill(255, 230, 120); NoStroke(); Circle(MouseX, MouseY, 20); } void DrawScene() { // The moon: a big pale circle with a soft halo behind it. NoStroke(); Fill(255, 255, 220, 30); Circle(650, 100, 160); Fill(250, 245, 220); Circle(650, 100, 90); // Grass: hundreds of thin lines, a little different each. RandomSeed(7); // same "random" grass every frame StrokeWeight(2); for (int x = 0; x < Width; x += 4) { Stroke(30, Random(70, 110), 60); float h = Random(30, 90); Line(x, Height, x + Random(-8, 8), Height - h); } } }
Circle(Random(Width), Random(300), 2) calls, before the moon.3. Animation: one firefly 5 min
Noise() is random that changes smoothly: neighbouring inputs give neighbouring outputs, so increasing t a little each frame makes the firefly wander instead of jump. Sin() swings between -1 and 1; scaled to 0..1 it makes the glow breathe. Two circles, a big faint one and a small bright one, look like light.
public class Fireflies : Sketch { float t; public override void Setup() { Size(800, 500); } public override void Draw() { Background(15, 20, 40); DrawScene(); // Noise() gives smooth random values: perfect for wandering. t += 0.005f; float x = Noise(t) * Width; float y = Noise(t + 100) * (Height - 120); // Sin() pulses between -1 and 1: the glow breathes. float glow = (Sin(FrameCount * 0.1f) + 1) / 2; // 0..1 NoStroke(); Fill(255, 230, 120, 40 * glow); Circle(x, y, 60); Fill(255, 240, 150, 120 + 135 * glow); Circle(x, y, 10); } void DrawScene() { NoStroke(); Fill(255, 255, 220, 30); Circle(650, 100, 160); Fill(250, 245, 220); Circle(650, 100, 90); RandomSeed(7); StrokeWeight(2); for (int x = 0; x < Width; x += 4) { Stroke(30, Random(70, 110), 60); Line(x, Height, x + Random(-8, 8), Height - Random(30, 90)); } } }
0.005f, or slow the breathing by changing 0.1f.4. Many fireflies: classes, lists and vectors 7 min
Forty fireflies need forty positions, so each one becomes an object of a small class, kept in a List. A PVector holds an x and a y and supports +, - and *, so moving is f.Pos += step. PVector.FromAngle() turns a noise value into a direction, and every firefly has its own noise Seed and glow Phase, so they don't move or blink together.
public class Fireflies : Sketch { // One class per "thing": each firefly keeps its own position and rhythm. class Firefly { public PVector Pos; public float Seed, Phase; } readonly List<Firefly> flies = new(); public override void Setup() { Size(800, 500); for (int i = 0; i < 40; i++) flies.Add(NewFly(Random(Width), Random(Height - 120))); } Firefly NewFly(float x, float y) => new Firefly { Pos = new PVector(x, y), Seed = Random(1000), Phase = Random(TWO_PI), }; public override void Draw() { Background(15, 20, 40); DrawScene(); float t = FrameCount * 0.01f; NoStroke(); foreach (var f in flies) { // Each one wanders by its own noise, and wraps around the edges. var step = PVector.FromAngle(Noise(f.Seed, t) * TWO_PI * 2) * 1.2f; f.Pos += step; f.Pos.X = (f.Pos.X + Width) % Width; f.Pos.Y = Constrain(f.Pos.Y, 10, Height - 100); float glow = (Sin(FrameCount * 0.08f + f.Phase) + 1) / 2; Fill(255, 230, 120, 40 * glow); Circle(f.Pos.X, f.Pos.Y, 50); Fill(255, 240, 150, 100 + 155 * glow); Circle(f.Pos.X, f.Pos.Y, 8); } } void DrawScene() { NoStroke(); Fill(255, 255, 220, 30); Circle(650, 100, 160); Fill(250, 245, 220); Circle(650, 100, 90); RandomSeed(7); StrokeWeight(2); for (int x = 0; x < Width; x += 4) { Stroke(30, Random(70, 110), 60); Line(x, Height, x + Random(-8, 8), Height - Random(30, 90)); } RandomSeed((int)Millis()); // back to real randomness } }
5. Interaction: mouse and keyboard 5 min
Methods like MousePressed() and KeyPressed() are called once per event: a click adds five fireflies, Space switches their mood. Inside Draw(), the vector from the mouse to each firefly decides how it reacts: SetMag() keeps the direction and sets the strength, which Map() makes stronger the closer the mouse is.
public class Fireflies : Sketch { class Firefly { public PVector Pos; public float Seed, Phase; } readonly List<Firefly> flies = new(); bool scared = true; public override void Setup() { Size(800, 500); for (int i = 0; i < 40; i++) flies.Add(NewFly(Random(Width), Random(Height - 120))); } Firefly NewFly(float x, float y) => new Firefly { Pos = new PVector(x, y), Seed = Random(1000), Phase = Random(TWO_PI), }; public override void Draw() { Background(15, 20, 40); DrawScene(); float t = FrameCount * 0.01f; var mouse = new PVector(MouseX, MouseY); NoStroke(); foreach (var f in flies) { var step = PVector.FromAngle(Noise(f.Seed, t) * TWO_PI * 2) * 1.2f; // Near the mouse: flee from it, or come closer -- Space switches. var away = f.Pos - mouse; float d = away.Mag(); if (d < 150) step += away.SetMag(Map(d, 0, 150, 3, 0)) * (scared ? 1 : -1); f.Pos += step; f.Pos.X = (f.Pos.X + Width) % Width; f.Pos.Y = Constrain(f.Pos.Y, 10, Height - 100); float glow = (Sin(FrameCount * 0.08f + f.Phase) + 1) / 2; Fill(255, 230, 120, 40 * glow); Circle(f.Pos.X, f.Pos.Y, 50); Fill(255, 240, 150, 100 + 155 * glow); Circle(f.Pos.X, f.Pos.Y, 8); } Fill(255, 150); Text($"{flies.Count} fireflies · click: add five · space: {(scared ? "shy" : "curious")}", 12, 20); } public override void MousePressed() { for (int i = 0; i < 5; i++) flies.Add(NewFly(MouseX + Random(-20, 20), MouseY + Random(-20, 20))); } public override void KeyPressed() { if (Key == ' ') scared = !scared; } void DrawScene() { NoStroke(); Fill(255, 255, 220, 30); Circle(650, 100, 160); Fill(250, 245, 220); Circle(650, 100, 90); RandomSeed(7); StrokeWeight(2); for (int x = 0; x < Width; x += 4) { Stroke(30, Random(70, 110), 60); Line(x, Height, x + Random(-8, 8), Height - Random(30, 90)); } RandomSeed((int)Millis()); } }
flies.Clear(), or make the moon follow the mouse.6. Sound, saving and sharing 5 min
One line of Play() gives each click a note, higher to the right. Save() writes a PNG of the canvas when you press S. The // @reel comments don't change how the sketch runs: they tell Sketch → Create reel... how to turn it into a short video, with the code typing itself scene by scene, then the sketch running with a simulated mouse. Export it as MP4 with sound, a GIF, or a YouTube thumbnail.
// @reel title: Fireflies in 60 lines of C# // @reel format: vertical // @reel result: 8 caption="Move the mouse · click to add more" // @reel scene "A firefly is a position, a seed and a rhythm" effect=type public class Fireflies : Sketch { class Firefly { public PVector Pos; public float Seed, Phase; } readonly List<Firefly> flies = new(); bool scared = true; public override void Setup() { Size(800, 500); for (int i = 0; i < 40; i++) flies.Add(NewFly(Random(Width), Random(Height - 120))); } Firefly NewFly(float x, float y) => new Firefly { Pos = new PVector(x, y), Seed = Random(1000), Phase = Random(TWO_PI), }; // @reel scene "Every frame: wander, avoid the mouse, glow" effect=lines public override void Draw() { Background(15, 20, 40); DrawScene(); float t = FrameCount * 0.01f; var mouse = new PVector(MouseX, MouseY); NoStroke(); foreach (var f in flies) { var step = PVector.FromAngle(Noise(f.Seed, t) * TWO_PI * 2) * 1.2f; // @mark var away = f.Pos - mouse; float d = away.Mag(); if (d < 150) step += away.SetMag(Map(d, 0, 150, 3, 0)) * (scared ? 1 : -1); // @mark f.Pos += step; f.Pos.X = (f.Pos.X + Width) % Width; f.Pos.Y = Constrain(f.Pos.Y, 10, Height - 100); float glow = (Sin(FrameCount * 0.08f + f.Phase) + 1) / 2; Fill(255, 230, 120, 40 * glow); Circle(f.Pos.X, f.Pos.Y, 50); Fill(255, 240, 150, 100 + 155 * glow); Circle(f.Pos.X, f.Pos.Y, 8); } } // @reel hide public override void MousePressed() { // A soft note for every new group, higher the further right you click. Play(Map(MouseX, 0, Width, 60, 84), synth: Synth.Pluck, amp: 0.5, release: 0.6); for (int i = 0; i < 5; i++) flies.Add(NewFly(MouseX + Random(-20, 20), MouseY + Random(-20, 20))); } public override void KeyPressed() { if (Key == ' ') scared = !scared; if (Key == 's') Save("fireflies.png"); } void DrawScene() { NoStroke(); Fill(255, 255, 220, 30); Circle(650, 100, 160); Fill(250, 245, 220); Circle(650, 100, 90); RandomSeed(7); StrokeWeight(2); for (int x = 0; x < Width; x += 4) { Stroke(30, Random(70, 110), 60); Line(x, Height, x + Random(-8, 8), Height - Random(30, 90)); } RandomSeed((int)Millis()); } } // @reel show
Where next: coming from Processing or p5.js, then every section of this reference ends with a runnable example.