Ponpon Mania is an animated comic featuring Ponpon, a megalomaniac sheep dreaming of becoming a DJ. We wanted to explore storytelling beyond traditional comics by combining playful interactions, smooth GSAP-powered motion, and dynamic visuals. The goal was to create a comic that feels alive, where readers engage directly with Ponpon’s world while following the narrative. The project evolved over several months, moving from early sketches to interactive prototypes.
About us
We are Justine Soulié (Art Director & Illustrator) and Patrick Heng (Creative Developer), a creative duo passionate about storytelling through visuals and interaction. Justine brings expertise in illustration, art direction, and design, while Patrick focuses on creative development and interactive experiences. Together, we explore ways to make stories more playful, immersive, and engaging.
Art Direction
Our visual direction emphasizes clean layouts, bold colors, and playful details. From the start, we wanted the comic to feel vibrant and approachable while using design to support the story. On the homepage, we aimed to create a simple, welcoming scene that immediately draws the user in, offering many interactive elements to explore and encouraging engagement from the very first moment.
The comic is mostly black and white, providing a simple and striking visual base. Color appears selectively, especially when Ponpon dreams of being a DJ and is fully immersed in his imagined world, highlighting these key moments and guiding the reader’s attention. Scroll-triggered animations naturally direct focus, while hover effects and clickable elements invite exploration without interrupting the narrative flow.
To reinforce Ponpon’s connection to music, we designed the navigation to resemble a music player. Readers move through chapters as if they were albums, with each panel functioning like a song. This structure reflects Ponpon’s DJ aspirations, making the reading experience intuitive, dynamic, and closely tied to the story.
Technical Approach
Our main goal was to reduce technical friction so we could dedicate our energy to refining the artistic direction, motion design, and animation of the website.
We used WebGL because it gave us full creative freedom over rendering. Even though the comic has a mostly 2D look, we wanted the flexibility to add depth and apply shader-based effects.
Starting from Justine’s illustrator files, every layer and visual element from each panel was exported as an individual image. These assets were then packed into optimized texture atlases using Free TexturePacker.

Once exported, the images were further compressed into GPU-friendly formats to reduce memory usage. Using the data generated by the packer, we reconstructed each scene in WebGL by generating planes at the correct size. Finally, everything was placed in a 3D scene where we applied the necessary shaders and animations to achieve the desired visual effects.

Tech Stack & Tools
Design
- Adobe Photoshop & Illustrator – illustration and asset preparation
- Figma – layout and interface design
Development
- ogl – WebGL framework for rendering
- Nuxt.js – frontend framework for structure and routing
- GSAP – animation library for smooth and precise motion
- Matter.js – physics engine used on the About page
- Free TexturePacker – for creating optimized texture atlases from exported assets
- Tweakpane – GUI tool for real-time debugging and fine-tuning parameters
Animating using GSAP
GSAP makes it easy to animate both DOM elements and WebGL objects with a unified syntax. Its timeline system brought structure to complex sequences, while combining it with ScrollTrigger streamlined scroll-based animations. We also used SplitText to handle text animations.
Home page
For the homepage, we wanted the very first thing users see to feel playful and full of life. It introduces the three main characters, all animated, and sets the tone for the rest of the experience. Every element reacts subtly to the mouse: the Ponpon mask deforms slightly, balloons collide softly, and clouds drift away in gentle repulsion. These micro-interactions make the scene feel tangible and invite visitors to explore the world of Ponpon Mania with curiosity and delight. We used GSAP timeline to choreograph the intro animation, allowing us to trigger each element in sequence for a smooth and cohesive reveal.
// Simple repulsion we used for the clouds in our render function
const dx = baseX - mouse.x;
const dy = baseY - mouse.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// Repel the cloud if the mouse is near
const radius = 2; // interaction radius
const strength = 1.5; // repulsion force
const repulsion = Math.max(0, 1 - dist / radius) * strength;
// Apply the repulsion with smooth spring motion
const targetX = basePosX + dx * repulsion;
const targetY = basePosY - Math.abs(dy * repulsion) / 2;
velocity.x += (targetX - position.x) * springStrength * deltaTime;
velocity.y += (targetY - position.y) * springStrength * deltaTime;
position.x += velocity.x;
position.y += velocity.y;
Chapter Selection
For the chapter selection, we wanted something simple yet evocative of Ponpon musical universe. Each chapter is presented as an album cover, inviting users to browse through them as if flipping through a record collection. We try to have a smooth and intuitive navigation, users can drag, scroll, or click to explore and each chapter snaps into place for an easy and satisfying selection experience.
Panel Animation
For the panel animations, we wanted each panel to feel alive bringing Justine’s illustrations to life through motion. We spent a lot of time refining every detail so that each scene feels expressive and unique. Using GSAP timelines made it easy to structure and synchronize the different animations, keeping them flexible and reusable. Here’s an example of a GSAP timeline animating a panel, showing how sequences can be chained together smoothly.
// Animate ponpons in sequence with GSAP timelines
const timeline = gsap.timeline({ repeat: -1, repeatDelay: 0.7 });
const uFlash = { value: 0 };
const flashTimeline = gsap.timeline({ paused: true });
function togglePonponGroup(index) {
ponponsGroups.forEach((g, i) => (g.mesh.visible = i === index));
}
function triggerFlash() {
const flashes = Math.floor(Math.random() * 2) + 1; // 1–2 flashes
const duration = 0.4 / flashes;
flashTimeline.clear();
for (let i = 0; i < flashes; i++) {
flashTimeline
.set(uFlash, { value: 0.6 }, i * duration) // bright flash
.to(uFlash, { value: 0, duration: duration * 0.9 }, i * duration + duration * 0.1); // fade out
}
flashTimeline.play();
}
ponponMeshes.forEach((ponpon, i) => {
timeline.fromTo(
ponpon.position,
{ y: ponpon.initialY - 0.2 }, // start slightly below
{
y: ponpon.initialY, // bounce up
duration: 1,
ease: "elastic.out",
onStart: () => {
togglePonponGroup(i); // show active group
triggerFlash(); // trigger flash
}
},
i * 1.6 // stagger delay between ponpons
);
});
About Page
On the About page, GSAP ScrollTrigger tracks the scroll progress of each section. These values drive the WebGL scenes, controlling rendering, transitions, and camera movement. This ensures the visuals stay perfectly synchronized with the user’s scrolling.
const sectionUniform = { progress: { value: 0 } };
// create a ScrollTrigger for one section
const sectionTrigger = ScrollTrigger.create({
trigger: ".about-section",
start: "top bottom",
end: "bottom top",
onUpdate: (self) => {
sectionUniform.progress.value = self.progress; // update uniform
}
});
// update scene each frame using trigger values
function updateScene() {
const progress = sectionTrigger.progress;
const velocity = sectionTrigger.getVelocity();
// drive camera movement with scroll progress
camera.position.y = map(progress, 0.75, 1, -0.4, 3.4);
camera.position.z =
5 + map(progress, 0, 0.3, -4, 0) +
map(progress, 0.75, 1, 0, 2) + velocity * 0.01;
// subtle velocity feedback on ponpon and camera
ponpon.position.y = ponpon.initialY + velocity * 0.01;
}
Thanks to the SplitText plugin, we can animate each section title line by line as it comes into view while scrolling.
// Split the text into lines for staggered animation
const split = new SplitText(titleDomElement, { type: "lines" });
const lines = split.lines;
// Create a timeline for the text animation
const tl = gsap.timeline({ paused: true });
tl.from(lines, {
x: "100%",
skewX: () => Math.random() * 50 - 25,
rotation: 5,
opacity: 0,
duration: 1,
stagger: 0.06,
ease: "elastic.out(0.7, 0.7)"
});
// Trigger the timeline when scrolling the section into view
ScrollTrigger.create({
trigger: ".about-section",
start: "top 60%",
end: "bottom top",
onEnter: () => tl.play(),
onLeaveBack: () => tl.reverse()
});
Page transitions
For the page transitions, we wanted them to add a sense of playfulness to the experience while keeping navigation snappy and fluid. Each transition was designed to fit the mood of the page so rather than using a single generic effect, we built variations that keep the journey fresh.
Technically, the transitions blend two WebGL scenes together using a custom shader, where the previous and next pages are rendered and mixed in real time. The animation of the blend is driven by GSAP tweens, which lets us precisely control the timing and progress of the shader for smooth, responsive transitions.
Designing Playful Experiences
Ponpon Mania pushed us to think beyond traditional storytelling. It was a joy to work on the narrative and micro-interactions that add playfulness and energy to the comic.
Looking ahead, we plan to create new chapters, expand Ponpon’s story, and introduce small games and interactive experiences within the universe we’ve built. We’re excited to keep exploring Ponpon’s world and share more surprises with readers along the way.
Thank you for reading! We hope you enjoyed discovering the creative journey behind Ponpon Mania and the techniques we used to bring Ponpon’s world to life.
If you want to follow Ponpon, check us out on TikTok or Instagram.
You can also support us on Tipeee!
Justine Soulié & Patrick Heng