The Starry Fields
von CJ Cepe @cjr_cepe
- 604
- 11
- 0
Introduction
I reinterpreted The Starry Night to celebrate its sky, stars, and swirling motion. I aimed to capture its energy and emotion, creating a version that reflects my vision. Generative art became my brush, allowing me to echo the painting’s spirit while weaving in my own creative voice.
Live Preview: https://cj-cepe.github.io/art-expi/


Supplies
I utilized the following tools & resources to accomplish my artwork:
- Hardware
- Desktop PC: my primary workstation
- Core Medium
- HTML, CSS, JavaScript: base languages of the piece
- Canvas API: digital canvas for rendering particles and motion
- Coding & Building
- VS Code: digital sketchbook for writing and testing code
- Live Preview extension: instant browser feedback
- npm + Esbuild: bundling scripts into a polished final output
- Git & GitHub: version control and for hosting the live artwork
- Preview & Testing
- Browsers (Chrome, Firefox): testing grounds for how the artwork behaved
- Documentation & Writing
- Obsidian: notebook for planning, drafting, and reflecting
- Google Gemini: checked math formulas, coding details, and grammar
- Supporting Tools
- Figma: preparing and editing images for this documentation
- References & Learning
- YouTube: painting analyses and topic and background studies
- MDN Web Docs: technical reference for JavaScript and Canvas API
- and some other sites 🫰

Quick Overview
How the Artwork Works
- This generative artwork is built using particles and a flow field. Each particle acts as a digital brushstroke. The flow field is an invisible map of forces that directs their movement. By adjusting internal parameters like speed, length, and curlScale, I can create dynamic, swirling patterns inspired by The Starry Night. The final result is a 2D animation rendered in a browser, where every frame is computed in real time.
My Creative Tips & Tricks ✨
- Start with a simple prototype first. Validate ideas before adding complexity
- Divide and conquer: Break down each problem into small, manageable parts. If a part is still big, break it down further, and solve it one step at a time
- Observe the source inspiration carefully, but translate it in your own way
- Keep experiments and notes organized as it helps with debugging and future iterations
- The most important thing is to love and believe in what you are creating
Image reference:
- https://www.youtube.com/watch?v=JlU3GskkcUw
- https://georgemsavva.github.io/creativecoding/posts/flowfields/
- https://www.youtube.com/watch?v=rB83DpBJQsE&t=159s

Phase 1 - Foundation Setup
This phase was about setting the direction of the project. I defined what I wanted to achieve, studied the background of the original artwork, explored tools and techniques, and prepared the core elements like the method and colors. The goal was to build a solid foundation before writing any code.
- Step 1: Goal and constraints
- My goal was to capture and re-express the turbulence and swirling textures of The Starry Night in a medium I could work with and share widely. I chose generative coding on the web because it matched both goals: creative flexibility and accessibility. A key constraint was that the work had to stay faithful to the textured, directional brushstroke quality of the painting while also being technically achievable within a browser.
- Step 2: Background study
- I studied the background of Vincent van Gogh and watched analyses of The Starry Night along with some of his other paintings. This was not just to understand the painting’s surface, but also to get a sense of where Van Gogh was coming from and how he expressed emotion through his brushwork. By doing this, I could aim to carry over the same expressive spirit in my own way. This step also acted as a guardrail, helping me stay respectful to the original while translating classic techniques into a modern digital format, and essentially making it my own
- Tip: If you are doing a similar project, studying the intent and background of the original can help guide your creative decisions while keeping your reinterpretation authentic
- Step 3: Tools and medium
- I wanted the artwork to exist in a medium that is accessible and easy to share, so I chose the web. From there, I explored different ways to draw in the browser, such as HTML Canvas, p5.js, and WebGL. I also studied related topics like requestAnimationFrame vs. setInterval, and 2D vs. WebGL contexts, to see what best fit the kind of artwork I envisioned. In the end, I settled on HTML Canvas because the project was mainly 2D, it worked consistently across browsers, and it provided a stable rendering base for building further.
- Step 4: Core technique
- I researched what mathematical ideas could best represent the movement and texture I wanted for the artwork. From observing The Starry Night and Van Gogh’s other works, I noticed the use of impasto and the strong sense of direction in each brushstroke. I thought of each stroke as an individual particle that could be simulated digitally. To model this, I looked into using a flow field as the backbone, since it allows particles to move in an organic, non-linear way. I considered noise functions like Perlin or Simplex as possible ways to generate the field, and also explored curl and divergence to generate swirling motions within the field, echoing the turbulence of the original painting
- Step 5: Colors
- I identified the main colors I wanted, focusing on blues, yellows, and a few highlights. I used a color picker to extract RGB values from Vincent van Gogh’s paintings, then listed them. After that, I adjusted the values to better match the mood I envisioned for the artwork. Getting the right color combination was important, because it set the emotional tone of the piece and connected the work to its artistic inspiration.


Phase 2 - the Core Engine
In this phase, I went straight to building the two core systems of the artwork: the moving element (the particle) and the force that directs it (the flow field). My goal here is to create a basic working version of a flow field, just enough to validate my idea before adding more complexity and visual polish.
- Step 1: Setup
- I initialized the basic files and tools: HTML, CSS, JavaScript, npm, and Git
- Step 2: Canvas and Grid
- Canvas: I created a canvas and initialized the 2D rendering context. The canvas is the digital sheet where everything is drawn, while the 2D rendering context handles the drawing
- Structured Code: I used Classes to represent the main components, one for the particle and one for the flow field
- Invisible Grid: I divided the canvas into an invisible grid of rows and columns based on the screen size and a chosen cellSize (for example, 20 pixels). This grid forms the backbone of the movement map
- Step 3: Particles and Field
- The Particles: Digital Brushstrokes:
- I created a particle class. Each particle act as digital brushstrokes with memory. Each particle keeps a history of its past positions. When it moves, the new position is added and the oldest one is removed. This creates a continuous trail that appears to fade as it flows
- The Field: The Invisible Map of Forces
- Next, I set up an invisible vector field to direct the particles. Each grid cell stores a vector (direction and magnitude). For this initial version, to keep it simple, I used a uniform flow from left to right just to see if the components work together
- Step 4 - Animation Loop
- I used requestAnimationFrame() to redraw the canvas many times per second. The loop updates particle positions and renders them smoothly. With particles, grid, flow field, and animation in place, I had a working prototype. It was simple but confirmed that the digital brushstrokes could be driven by a map of forces


Phase 3 - Shaping the Art
This phase is where I transformed the raw technical foundation from Phase 2 into the artwork I envisioned. It was the longest phase because every effect I wanted (like a swirl in the middle, or a denser flow in certain areas), is in its own is a problem to solve.
- Steps I followed for each problem:
- Step 1: I identify the effect I wanted to see in the artwork (e.g., representing the turbulence with a curl) or the performance problem to be solved (e.g., reducing lagging) then translate it into a technical problem
- Step 2: I make an attempt to implement a solution based on my current knowledge and understanding, and as much as possible with simple solution
- Step 3: If the initial implementation was lacking, I would inquire and query from different resources to formulate a solution
- Step 4: I then apply the informed solution and refactor the existing code (reorganized and cleaned), to integrate the new feature
- Step 5: Lastly run a visual and performance tests
- Repeat the problem-solving cycle for each new problem
- Some problems I solved in this phase:
- Achieving Uniform Particle Distribution
- Problem: Since particles are positioned randomly in the canvas, there are areas that are less dense or visually "gappy"
- Solution: I shuffled the grid cell list before spawning. I also added a jitter value (a random positional offset), in the computation to avoid perfectly aligned particles making the particles appear naturally and organically scattered across the canvas.
- Enhancing Visual Performance
- Problem: The artwork is lagging in some devices. My initial approach is computationally demanding
- Solution: I reduced and adjusted some parameters like particle count and cell size. I also simplified per-frame calculations and used whole numbers instead of many decimals as suggested by an online reference about HTML canvas optimization. Lastly, I refactored the logic behind the trailing opacity of each particle. Instead, a Double-Buffering (using a hidden, off-screen canvas), is implemented to create a fading trail effect by drawing a semi-transparent layer over the buffer each frame.
- Swirl effect
- Problem: I wanted to have some points where it is like wind spiraling
- The Idea: Introduce rotational force called curl into the field to generate complex, non-linear movement
- The Solution: The core implementation involved taking the current direction vector and calculating a new vector that is perpendicular (90 degrees rotated) to it. This forced the movement into the required circular and turbulent paths. Positive and negative values were used to control the direction of the swirl (clockwise vs counter-clockwise rotation)



Phase 4 - Artistic Tuning
This phase was all about experimentation and refinement. I adjusted internal parameters to shape the final look of the artwork. I'm no longer fixing bugs in here but focusing fully on the visual outcome. Each parameter directly influenced the visual result, so the process was iterative and precise. This was my favorite phase btw.
Some of the parameters I worked with:
- particle count - total number of digital brushstrokes active at once. Higher values create denser visuals but also require more computation
- speed - base velocity of particle movement along the flow. Small changes here affect rhythm and motion
- maxLength - the maximum number of coordinates a particle remembers before deleting the oldest point. Longer lengths create flowing trails, shorter lengths feel sharp and quick. In this version I used shorter lengths to create a twinkling background effect
- cellSize - size of each invisible square in the grid. smaller cells add detail, larger cells smooth it out
- waveAmplitude - height of background waves
- waveFrequency - frequency of background waves (smaller = smoother)
- curlScale - dictates the size of the swirls
Note: the current version of the artwork uses fixed field values that I picked for stable presentation. It could also be modified to assign random values on each reload, producing a unique result every time.



Phase 5 - Finalization & Deployment
This phase was about wrapping up the project: cleaning the code, optimizing the build, and packaging everything for presentation. The focus shifted from creating new features to making the work stable, accessible, and ready to share.
- Step 1: Code Structure & Cleanup
- I performed a final cleanup of the project, removing unnecessary files, comments, and temporary code. The source was restructured into a maintainable directory format so future edits or experiments could be done without clutter
- Step 2: Packaging & Hosting
- I bundled the multiple scripts into a single optimized file using esbuild, ensuring faster loading and easier deployment. The finished build was uploaded to a GitHub repository and deployed for live access, making the project easily accessible and shareable.
- Step 3: Documentation & Presentation
- I wrote a simple documentation describing the project’s concept, technical process, and development phases (the one you’re reading now). This serves both as a record of my workflow and as a guide for anyone interested in the techniques behind the artwork
- Completion
- The last step, the project reached its intended outcome.
- I'm done yay! 🎉


Final Output
This section showcases the final output of the artwork. I experimented with different parameter combinations and different screen sizes to explore how the visuals respond.
Regards
- Thank you for taking the time to read through this documentation. I hope it gave you insight into my creative and technical process. Whether you are an artist, a coder, or simply curious, I hope this inspires you to explore your own projects and experiment boldly. Remember, creativity is a journey. Trust your instincts, enjoy the process, and don’t be afraid to try new things.
- Happy creating! 🧑🎨🎨🖌️
Live Website: https://cj-cepe.github.io/art-expi/
Github Repo: https://github.com/CJ-Cepe/art-expi





+0 Kommentare
Melden Sie sich an oder melden Sie sich kostenlos an, um zu kommentieren