AI Neon Sign - Type Your Own Glowing Text

This sketch turns any text you type into a glowing neon sign, complete with a flickering animated glow and a dark brick wall backdrop. Click anywhere to cycle through three vibrant neon color palettes, and the sign resizes automatically to fill your browser window.

🧪 Try This!

Experiment with the code by making these changes:

  1. Swap the color palette — Replace the three neon colors with your own choices to change what appears when clicking through the sign.
  2. Make the glow softer and thicker — Doubling the number of glow layers makes the halo around the text look smoother and more intense.
  3. Change the default sign text — The input box starts pre-filled with a word of your choice instead of 'NEON'.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a realistic neon sign effect out of whatever text you type into an input box, complete with a soft pulsing glow and a dark brick wall behind it. It works by drawing the same word many times on top of itself, each copy slightly larger and more transparent than the last, which is a classic trick for faking glow without any image filters. Perlin noise makes the glow gently flicker like a real neon tube, and clicking the mouse instantly swaps the color palette between pink, cyan, and green.

The code is short but teaches several important p5.js ideas at once: DOM elements like createInput() for live text entry, alpha transparency for layering glow, noise() for organic animation, and nested for-loops for generating a repeating pattern (the brick wall). By studying it you'll learn how a handful of loops and a well-chosen color palette can produce an effect that looks far more complex than the code itself.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-window canvas, builds a text input box pre-filled with the word 'NEON', and defines an array of three neon colors to cycle through
  2. Every frame, draw() first calls drawBricks() to paint a dark, staggered brick pattern as the background
  3. draw() then reads whatever text is currently in the input box and calculates a base text size along with a flicker value driven by Perlin noise
  4. A loop runs 12 times, drawing the same text over and over at increasing sizes with decreasing opacity - this stack of transparent layers is what creates the soft glowing halo around the letters
  5. Clicking the mouse triggers mousePressed(), which advances to the next color in the palette array so the neon sign changes color
  6. If the browser window is resized, windowResized() rebuilds the canvas to match the new dimensions so the sign always fills the screen

🎓 Concepts You'll Learn

DOM input elements with createInput()Faking glow with layered alpha transparencyPerlin noise for organic flicker animationNested for-loops for pattern generationColor arrays and cycling stateResponsive canvas with windowResized()

📝 Code Breakdown

setup()

setup() runs once at the start and is the right place to configure the canvas, create DOM elements like inputs, and initialize arrays that won't change shape later - only their contents (like which color is selected) change over time.

function setup() {
  createCanvas(windowWidth, windowHeight);
  textAlign(CENTER, CENTER); textStyle(BOLD); noStroke();
  inp = createInput("NEON"); inp.position(20, 20); inp.size(200);
  cols = [color(255, 60, 200), color(0, 255, 255), color(0, 255, 120)];
}
Line-by-line explanation (4 lines)
createCanvas(windowWidth, windowHeight);
Makes the canvas fill the entire browser window.
textAlign(CENTER, CENTER); textStyle(BOLD); noStroke();
Sets text to be centered both horizontally and vertically, makes it bold for a chunkier neon look, and disables outlines so shapes only use fill color.
inp = createInput("NEON"); inp.position(20, 20); inp.size(200);
Creates an HTML text input box pre-filled with the word 'NEON', places it near the top-left corner, and sets its width to 200 pixels. This is a p5.js DOM element, not something drawn on the canvas.
cols = [color(255, 60, 200), color(0, 255, 255), color(0, 255, 120)];
Builds an array of three p5.Color objects - hot pink, cyan, and green - that the sign will cycle through when clicked.

draw()

draw() runs continuously and is where all animation logic lives. This function shows a common technique: instead of using a real blur filter, you simulate glow by drawing multiple transparent copies of a shape at increasing sizes - a trick that works for text, lights, particles, and more.

🔬 This loop draws 12 overlapping copies of your text to fake a glow. What happens if you change the pow() exponent from 1.5 to 4 (glow becomes tight and concentrated near the letters) or to 0.5 (glow spreads out evenly)?

  for (let i = 12; i > 0; i--) {
    let a = 255 * pow(i / 12, 1.5) * f;
    fill(red(c), green(c), blue(c), a);
    textSize(base + i * 4);
    text(t, width / 2, height / 2);
  }
function draw() {
  drawBricks();
  let t = inp.value() || " ";
  let base = min(width, height) * 0.18, f = map(noise(frameCount * 0.05), 0, 1, 0.7, 1.1);
  let c = cols[ci];
  for (let i = 12; i > 0; i--) {
    let a = 255 * pow(i / 12, 1.5) * f;
    fill(red(c), green(c), blue(c), a);
    textSize(base + i * 4);
    text(t, width / 2, height / 2);
  }
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

for-loop Glow Layer Stack for (let i = 12; i > 0; i--) { ... }

Draws the same text 12 times at increasing sizes with decreasing opacity, layering translucent copies to simulate a glowing halo.

drawBricks();
Calls the helper function that clears the frame and paints the brick wall pattern behind everything else.
let t = inp.value() || " ";
Grabs the current text typed into the input box; if the box is empty, falls back to a single space so text() never fails on an empty string.
let base = min(width, height) * 0.18, f = map(noise(frameCount * 0.05), 0, 1, 0.7, 1.1);
Calculates a base font size relative to the smaller screen dimension, and computes a flicker factor 'f' using Perlin noise that slowly drifts between 0.7 and 1.1 over time - this is what makes the glow pulse instead of staying static.
let c = cols[ci];
Looks up the currently selected color from the palette array using the index ci, which changes when the mouse is clicked.
for (let i = 12; i > 0; i--) {
Starts a loop counting down from 12 to 1, drawing 12 layered copies of the text - looping backwards means the biggest, most transparent layer is drawn first, then progressively smaller and more opaque layers are drawn on top.
let a = 255 * pow(i / 12, 1.5) * f;
Calculates the opacity (alpha) for this layer. Because i/12 shrinks as the loop counts down, later (smaller) layers get higher alpha, making the core of the text solid while outer layers fade out. The pow(...,1.5) curve controls how sharply that fade happens, and 'f' applies the noise-based flicker.
fill(red(c), green(c), blue(c), a);
Sets the fill color to the current neon color but with this layer's calculated transparency applied.
textSize(base + i * 4);
Makes each outer layer slightly bigger than the last by adding 4 pixels per loop step, which is what spreads the glow outward from the core text.
text(t, width / 2, height / 2);
Draws the text centered on the canvas at this layer's size and transparency.

drawBricks()

drawBricks() demonstrates how nested for-loops can generate a repeating 2D pattern, and how a modulo operator (% 2) can alternate behavior between rows or columns - a technique useful for checkerboards, tile grids, and brick walls alike.

🔬 The (y / 40) % 2 ? 40 : 0 part offsets every other row to create the staggered brick look. What happens if you replace it with just 0 so every row lines up perfectly?

  for (let y = 0; y < height + 40; y += 40) {
    for (let x = (y / 40) % 2 ? 40 : 0; x < width + 80; x += 80) rect(x, y, 80, 40);
  }
function drawBricks() {
  background(10, 10, 15);
  fill(25); stroke(15); strokeWeight(2);
  for (let y = 0; y < height + 40; y += 40) {
    for (let x = (y / 40) % 2 ? 40 : 0; x < width + 80; x += 80) rect(x, y, 80, 40);
  }
  noStroke();
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

for-loop Brick Row Loop for (let y = 0; y < height + 40; y += 40) {

Steps down the canvas 40 pixels at a time, one iteration per row of bricks.

for-loop Brick Column Loop for (let x = (y / 40) % 2 ? 40 : 0; x < width + 80; x += 80) rect(x, y, 80, 40);

Draws each brick rectangle across the row, starting at an offset of either 0 or 40px depending on whether the row is even or odd, which staggers the bricks like a real wall.

background(10, 10, 15);
Clears the entire canvas each frame with a near-black navy color, forming the mortar/gap color between bricks.
fill(25); stroke(15); strokeWeight(2);
Sets the brick fill to dark gray, the outline color to almost-black, and makes outlines 2 pixels thick so brick edges are visible.
for (let y = 0; y < height + 40; y += 40) {
Loops down the canvas in 40-pixel steps (the brick height), going a little past the bottom edge so no gaps appear.
for (let x = (y / 40) % 2 ? 40 : 0; x < width + 80; x += 80) rect(x, y, 80, 40);
For each row, checks whether the row number (y/40) is odd or even using the modulo operator; odd rows start 40px further right than even rows, which offsets alternating rows and creates the classic staggered brick pattern. Each rect() draws one 80x40 brick.
noStroke();
Turns outlines back off after the bricks are drawn, so the neon text drawn afterward in draw() doesn't get an unwanted outline.

mousePressed()

mousePressed() is a p5.js event function that automatically runs whenever the mouse is clicked anywhere on the canvas. It's the simplest way to add interactivity without tracking click state yourself.

function mousePressed() { ci = (ci + 1) % cols.length; }
Line-by-line explanation (1 lines)
ci = (ci + 1) % cols.length;
Increments the color index and wraps it back to 0 once it passes the last color, using the modulo operator - this is the standard pattern for cycling through an array's items in a loop.

windowResized()

windowResized() is another built-in p5.js event function, automatically called whenever the browser window changes size. Pairing it with resizeCanvas() keeps full-screen sketches responsive.

function windowResized() { resizeCanvas(windowWidth, windowHeight); }
Line-by-line explanation (1 lines)
resizeCanvas(windowWidth, windowHeight);
Rebuilds the canvas to match the browser window's current width and height whenever the window is resized.

📦 Key Variables

inp object

Holds the p5.Element reference to the HTML text input box, used to read whatever the user has typed.

let inp;
cols array

An array of p5.Color objects representing the available neon color palette that the sign cycles through.

let cols;
ci number

The current index into the cols array, tracking which neon color is active; increments on each mouse click.

let ci = 0;

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG draw()

Long text input has no width constraint or text wrapping, so typing a long sentence will overflow off both sides of the canvas and become unreadable.

💡 Dynamically shrink textSize based on the string length, e.g. using textWidth(t) to check if the rendered text exceeds the canvas width and scaling 'base' down accordingly.

PERFORMANCE draw()

The text() call runs 12 times every single frame at 60fps, which is 720 text draws per second - expensive for longer strings or lower-end devices.

💡 Consider drawing the glow layers to an offscreen createGraphics() buffer only when the text or color changes, then just image() that buffer each frame instead of redrawing text 12 times per frame.

STYLE draw() and drawBricks()

Magic numbers like 12, 0.18, 1.5, 40, and 80 are scattered through the code with no explanation, making it harder to tweak or understand at a glance.

💡 Extract these into named constants at the top of the file, e.g. const GLOW_LAYERS = 12; const BRICK_SIZE = 40;, so their purpose is clear and they're easy to tune in one place.

FEATURE mousePressed()

The only way to change color is clicking anywhere on the canvas, which could be confusing since it also might feel unrelated to the text input.

💡 Add a visible color-swatch button or keyboard shortcut (e.g. pressing 'C') so the color-changing interaction is more discoverable, and let clicking the canvas do nothing so it doesn't compete with focusing the input box.

🔄 Code Flow

Code flow showing setup, draw, drawbricks, mousepressed, windowresized

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> glowloop[Glow Layer Stack] glowloop --> draw draw --> drawbricks[drawBricks] drawbricks --> brickrows[Brick Row Loop] brickrows --> brickcolumns[Brick Column Loop] brickcolumns --> draw click setup href "#fn-setup" click draw href "#fn-draw" click glowloop href "#sub-glow-loop" click drawbricks href "#fn-drawbricks" click brickrows href "#sub-brick-rows" click brickcolumns href "#sub-brick-columns"

❓ Frequently Asked Questions

What visual effects does the AI Neon Sign sketch create?

The sketch transforms user-input text into a glowing neon sign effect, set against a dark brick wall background, enhancing the vibrant nightlife atmosphere.

How can users interact with the AI Neon Sign sketch?

Users can type their own text to create a custom neon sign and click the canvas to cycle through different vibrant neon colors.

What creative coding techniques are demonstrated in this sketch?

This sketch showcases techniques like dynamic text rendering, color cycling, and procedural generation using noise to create a glowing effect.

Preview

AI Neon Sign - Type Your Own Glowing Text - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of AI Neon Sign - Type Your Own Glowing Text - Code flow showing setup, draw, drawbricks, mousepressed, windowresized
Code Flow Diagram