Skip to content

cevikebru/My-Text-To-Art-p5.js-Project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

My-Text-To-Art-p5.js-Project

📍 Description:

Text-to-Art is an interactive generative art project that transforms written text into animated, abstract visual compositions.

The main goal of the project is to explore how language can act as a semantic seed for algorithmic design and how textual properties such as characters, length, rhythm, and variability can influence visual parameters like color, symmetry, motion, and composition.

Another goal of the project was for users to be able to interact in the project area(Projektmesse) on 02.02.2026 and obtain individualized outputs with the input they provide.

The project is implemented using HTML, CSS, and JavaScript, with p5.js as the core creative coding library. It runs entirely in the browser and requires no backend. Each artwork is generated dynamically based on user input and includes controlled randomness, ensuring that no two results are ever exactly the same, even when the same text is entered multiple times.

Project Structure and File Overview

Input page ->index.html 💻👋

This file is the entry point of the project. It presents the landing page where users can enter a word, sentence, or number. The page itself does not generate the artwork; instead, it collects the user input and stores it in localStorage. A secondary option, “Surprise me”, selects a predefined random prompt. After submission, the user is redirected to the result view.

  • The following function retrieves the user input, validates it, stores it in localStorage, and redirects the user to the result page:
 function createArt(surprise = false) {
 const input = document.getElementById("userText");
 const val = surprise
   ? generatePrompt()
   : (input.value || "").trim();

 if (!val) {
   input.focus();
   return alert("Please enter something first.");
 }

 // Store user input and a variation seed
 localStorage.setItem("userText", val);
 localStorage.setItem("variationSeed", Math.random().toString());

 // Redirect to the result page
 window.location.href = "result.html";
}
  • To support "surprise me" button, a set of predefined prompts is used:
 function generatePrompt() {
            const ideas = [
                "Embrace the flow",
                "42",
                "You have to be patient",
                "Mandala art",
                "This was a random prompt",
                "A secret you never said aloud",
                "University of Bamberg",
                "Matrix",
                "I love programming!",
                "This is Inf-Einf-B!",
                "Projektmesse",
            ];

            return ideas[Math.floor(Math.random() * ideas.length)];
        }

Art logic -> sketch.js🎀

The generative system is implemented using p5.js and is driven by the user’s text input. Instead of rendering a static image, the system transforms semantic input into a dynamic visual seed, ensuring that the same input can still produce different results.

🗂 Key Variables in sketch.js

These are the core variables controlling canvas, text input, color, randomness, and animation state:

// Global variables controlling canvas, text input, color palette, randomness, and animation state.
let txt;            // User's text input
let cnv;            // Main canvas where artwork is drawn
let t = 0;          // Time counter (frame-based)
let maxT = 50;      // Duration of the render (number of frames)
let variationSeed = 0; // Additional random seed for non-deterministic variation
let glowGraphics;   // Secondary canvas for glow and blur effects
let bgColor = 6;    // Background hue in HSB color space
let seedBase = 1;   // Main numeric seed derived from user text
let runEntropy = Math.floor(Math.random() * 1_000_000_000); // One-time random number for extra variation
let palette = [];   // Color palette for the generative artwork
let artworkId = ""; // Unique identifier for each generated artwork

The generative process is divided into two main steps:

1️⃣ Transforming text into a numeric seed:

  • The user’s input text is converted into a numeric value by iterating over its characters. This value is later combined with a random variation seed to avoid deterministic output.
function textToSeed(text) {
  let seed = 0;
  for (let i = 0; i < text.length; i++) {
    seed += text.charCodeAt(i) * (i + 1);
  }
  return seed;
}

2️⃣ Using this seed to control motion, color, and symmetry:

  • The following function represents the core visual logic. Using rotation, symmetry, and color variation, it creates a mandala-like generative form that evolves over time.
function drawPattern(seed, t) {
  randomSeed(seed);
  noiseSeed(seed);

  translate(width / 2, height / 2);

  const layers = 24;
  for (let i = 0; i < layers; i++) {
    rotate(TWO_PI / layers);

    const radius = noise(i, t) * 200;
    const hue = (seed + i * 10) % 360;

    stroke(hue, 80, 90, 60);
    noFill();

    beginShape();
    for (let a = 0; a < TWO_PI; a += 0.2) {
      const r = radius + noise(a, i) * 50;
      vertex(cos(a) * r, sin(a) * r);
    }
    endShape(CLOSE);
  }
}

🔁 Conceptual Workflow

User Input (Text)
        ↓
Text-to-Seed Conversion
        ↓
Random Variation Seed
        ↓
Generative Drawing Logic (p5.js)
        ↓
Unique Animated Artwork

💾 Saving Artworks: saveArt() & generateArtworkId()

This section handles how each piece of generative art is saved and tracked.
When the user clicks "Save", the artwork is first converted into a thumbnail for the gallery, then stored in localStorage as a JSON object.
Each artwork is assigned a unique ID using the generateArtworkId() function, combining a timestamp with a random number to ensure uniqueness.

If the browser cannot use localStorage (for example, on file:// URLs), a fallback via window.name ensures the gallery still works.
Finally, the full-resolution image is downloaded as a PNG, and the page redirects to the gallery to immediately display the new artwork.

function saveArt() {
  const thumbData = makeThumbnailDataURL(cnv.elt, 640, 0.84);

  let arts = [];
  try {
    arts = JSON.parse(localStorage.getItem("artGallery") || "[]");
    if (!Array.isArray(arts)) arts = [];
  } catch (e) {
    arts = [];
  }

  const entry = {
    id: artworkId,
    thumb: thumbData,
    text: txt,
    createdAt: Date.now()
  };

  arts.push(entry);

  const serialized = JSON.stringify(arts);

  // Save to localStorage
  try { localStorage.setItem("artGallery", serialized); } 
  catch (e) { console.warn("localStorage failed, using window.name fallback"); }

  // file:// fallback
  try { window.name = JSON.stringify({ artGallery: arts }); } 
  catch (e) {}

  // Save full-resolution PNG
  saveCanvas(cnv, `textart-${artworkId}`, "png");

  // Redirect to gallery after a short delay
  setTimeout(() => {
    window.location.href = `gallery.html?new=${encodeURIComponent(artworkId)}`;
  }, 250);
}

// Generate unique ID for each artwork
function generateArtworkId() {
  return (
    Math.floor(Date.now() / 1000).toString(36) +
    "-" +
    Math.floor(Math.random() * 1e6).toString(36)
  ).toUpperCase();
}

Background animation->bg-sketch.js🎨

This script creates a subtle, animated background for the main page using p5.js instance mode.

  • The background consists of soft flowing lines and small drifting particles, designed to be low-contrast so it doesn't distract from the main content.
  • dots array stores individual particles with random position, size, speed, and opacity.
  • p.setup() initializes the canvas to match the page size and sets color mode to HSB for vibrant effects.
  • p.draw() animates the lines and particles over time using Perlin noise, creating a slow-moving, natural flow effect.
  • p.windowResized() ensures the background resizes dynamically with the browser window.

Key Features

1. Canvas Setup

  • Full-screen canvas attached to the #bg element.
p.setup = () => {
  w = mount.clientWidth || p.windowWidth;
  h = mount.clientHeight || p.windowHeight;
  const c = p.createCanvas(w, h);
  c.parent(mount);
  p.colorMode(p.HSB, 360, 100, 100, 100);
  init();
};

2. Flow Field Lines

  • Generates soft, wavy lines across the background.
  • Uses Perlin noise for natural, smooth motion.
for (let i = 0; i < lines; i++) {
  p.beginShape();
  for (let s = 0; s <= steps; s++) {
    const n = p.noise(x * 0.002, y0 * 0.003, t);
    const y = y0 + (n - 0.5) * 90;
    p.curveVertex(x, y);
  }
  p.endShape();
}

3. Particle Movement

  • Dots move smoothly with noise-based velocities.
  • Motion across edges.
  • Each dot has random size, speed, and opacity for a subtle effect.
for (const d of dots) {
  d.x += (n - 0.5) * d.s;
  d.y += (p.noise(...) - 0.5) * d.s;
  // wrap-around screen edges
  if (d.x < -10) d.x = w + 10;
  if (d.x > w + 10) d.x = -10;
  ...
  p.circle(d.x, d.y, d.r);
}

Result Page -> result.html 🎨

This page displays the generative artwork created from the user’s input text using p5.js.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Text-to-Art • Result</title>
    <link rel="stylesheet" href="style.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
    <script src="sketch.js"></script>
  </head>
  <body>
    <div class="overlay">
      <div class="pill" id="pill-text">Input: <strong id="pill-input"></strong></div>
      <div class="controls">
        <button class="save-btn" onclick="saveArt()">Save to gallery</button>
        <a class="save-btn" href="gallery.html">Open gallery</a>
        <a class="save-btn" href="index.html">← Back / Create new</a>
      </div>
    </div>
    <div id="loading" class="loading">
    <span>Blooming your art, wait a little…</span>
  </div>
  </body>
</html>

Key Features

1. Artwork Rendering

  • Uses sketch.js to render the art on the canvas.
  • Visuals are unique per input thanks to deterministic and random seeds.
  • Shows a subtle loading message: Blooming your art, wait a little… 🌱

2. User Interaction

  • Save button 💾 → saves the generated artwork to the gallery.
  • Gallery link 🖼 → view all saved artworks.
  • Back button ← → return to home page to create new art.

3. Input Display

  • Shows the user’s original text and generated artwork ID in a “pill” at the top.
  • Keeps a clear connection between input and output.

Gallery Page -> gallery.html 🖼️

This page shows all previously saved artworks from the user. It retrieves them from localStorage (or window.name as a fallback).


Key Features

1. Display Saved Artworks

  • Each artwork is shown as a card with:
    • Thumbnail image
    • Unique ID
    • Original text input used to generate it
  • Clicking a card opens a larger view in a new tab.
 const gallery = document.getElementById("gallery");
        const empty = document.getElementById("empty");
        gallery.innerHTML = "";

        if (!arts.length) {
          empty.style.display = "block";
          return;
        }

        empty.style.display = "none";

        arts
          .slice()
          .reverse()
          .forEach((art) => {
            if (!art.thumb) return;

            const card = document.createElement("div");
            card.className = "art-card";

            card.innerHTML = `
              <img src="${art.thumb}" alt="Generated artwork">
              <div class="meta"><strong>ID:</strong> ${art.id}</div>
              <div class="meta"><strong>Input:</strong> ${art.text}</div>
            `;

            card.onclick = () => {
              const w = window.open("", "_blank");
              w.document.write(`
                <img src="${art.thumb}" style="width:100%">
                <p><strong>ID:</strong> ${art.id}</p>
                <p><strong>Input:</strong> ${art.text}</p>
              `);
            };

            gallery.appendChild(card);
          });
      }

2. User Controls

  • Create new ➕ → back to the home page to generate a new artwork.
  • Clear gallery 🗑️ → removes all saved artworks with confirmation.
//clear gallery function
      function clearGallery() {
        const confirmed = confirm("Clear all saved artworks?");
        if (!confirmed) return;

        localStorage.removeItem("artGallery");
        try {
          window.name = "";
        } catch (e) {}

        loadGallery();
      }

3. Storage Handling

  • Reads artwork data from localStorage first.
  • If empty, tries window.name as a file:// fallback.
  • Keeps the gallery in sync with local storage .
 /* localStorage JSON */
        try {
          const raw = localStorage.getItem("artGallery");
          if (raw) {
            const parsed = JSON.parse(raw);
            if (Array.isArray(parsed)) arts = parsed;
          }
        } catch (e) {
          arts = [];
        }

        /*  file:// fallback  */
        if (!arts.length && window.name) {
          try {
            const backup = JSON.parse(window.name);
            if (backup && Array.isArray(backup.artGallery)) {
              arts = backup.artGallery;
            }
          } catch (e) {
            arts = [];
          }
        }

Style Guide -> style.css

The Text-to-Art project uses a dark, minimal aesthetic to highlight generative visuals. Most of the design, spacing, and layout decisions were made using AI tools (cursor + Figma) for rapid prototyping.


1. Global Colors & Variables 🌈

:root {
  --bg: #050507;           /* Main dark background */
  --panel: rgba(255,255,255,0.04);  /* Light translucent panels */
  --accent: #7bf1c8;       /* Primary gradient accent */
  --accent-2: #82a0ff;     /* Secondary gradient accent */
  --text: #f8f8fb;         /* Main text color */
  --muted: #aeb1c5;        /* Secondary, muted text */
  --border: rgba(255,255,255,0.12);
  --radius: 16px;           /* Rounded corners */
  --shadow: 0 20px 60px rgba(0,0,0,0.45); /* Soft shadows for cards */
}

2. Inputs & Buttons ✍️🖱️

input {
  padding: 14px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--panel);
  transition: border 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}

input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(123,241,200,0.25);
  transform: translateY(-1px);
}

button.primary {
  background: linear-gradient(120deg, var(--accent), var(--accent-2));
  color: #041014;
  box-shadow: 0 12px 35px rgba(130,160,255,0.25);
}

3. Hero Layout & Typography

.hero {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 32px;
}

.hero-text h1 {
  font-size: clamp(36px, 5vw, 52px);
  line-height: 1.1;
  letter-spacing: -0.02em;
}
  • Uses CSS Grid for responsive layout
  • clamp() ensures text scales nicely on mobile and desktop

4. Gallery cards

.art-card {
  background: var(--panel);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.art-card img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 12px;
  border: 1px solid var(--border);
}
  • Cards are modular and highlight artwork
  • Rounded corners + shadows make images pop

5. Responsive Design

@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; }
  .controls { flex-wrap: wrap; justify-content: flex-start; }
}
  • Layout adapts for smaller screens
  • Buttons & text scale naturally

About

This is my first project in GitHub, hello, world! hello, GitHub! :3 . This is a bonus point project I created for the introductory programming course at my university.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors