From c56a33b1a7808737ccf8e94e210445c820bde003 Mon Sep 17 00:00:00 2001
From: Chris Lally <24978693+ChrisLally@users.noreply.github.com>
Date: Fri, 30 Jan 2026 12:32:35 -0500
Subject: [PATCH] Feature - I added a modern PixiJS v8 foundation demo with
physics particles (FConsole preview)
---
src/modern-pixi8/.gitignore | 3 +
src/modern-pixi8/README.md | 56 ++++++
src/modern-pixi8/index.html | 133 ++++++++++++++
src/modern-pixi8/package.json | 22 +++
src/modern-pixi8/src/Game.ts | 296 ++++++++++++++++++++++++++++++++
src/modern-pixi8/src/main.ts | 81 +++++++++
src/modern-pixi8/tsconfig.json | 16 ++
src/modern-pixi8/vite.config.ts | 15 ++
8 files changed, 622 insertions(+)
create mode 100644 src/modern-pixi8/.gitignore
create mode 100644 src/modern-pixi8/README.md
create mode 100644 src/modern-pixi8/index.html
create mode 100644 src/modern-pixi8/package.json
create mode 100644 src/modern-pixi8/src/Game.ts
create mode 100644 src/modern-pixi8/src/main.ts
create mode 100644 src/modern-pixi8/tsconfig.json
create mode 100644 src/modern-pixi8/vite.config.ts
diff --git a/src/modern-pixi8/.gitignore b/src/modern-pixi8/.gitignore
new file mode 100644
index 0000000..320c107
--- /dev/null
+++ b/src/modern-pixi8/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+dist/
+package-lock.json
diff --git a/src/modern-pixi8/README.md b/src/modern-pixi8/README.md
new file mode 100644
index 0000000..8a8d0a9
--- /dev/null
+++ b/src/modern-pixi8/README.md
@@ -0,0 +1,56 @@
+# PixiJS v8 + FConsole Foundation Demo
+
+A high-performance, modern graphics demonstration showcasing the **Flashist Foundation** stack. This project serves as a reference implementation for **PixiJS v8** integrated with **FConsole** for real-time debugging and performance monitoring.
+
+## 🚀 Key Features
+
+- **Native PixiJS v8**: Built from the ground up to utilize the latest PixiJS v8 features, including the new rendering pipeline (WebGPU/WebGL).
+- **Physics-Based Interactivity**: A custom particle simulation using vector physics for fluid, organic movement.
+- **Neon Bloom Aesthetics**: Leveraging PixiJS filters and additive blending to create a premium, "next-gen" visual style.
+- **FConsole Integration (Preview)**: Pre-configured for real-time debugging. Note: Full runtime integration is pending an update to the `@flashist/fconsole` package for PixiJS v8 support.
+- **Modern Tooling**: Powered by **Vite** and **TypeScript** for the fastest possible development cycle and type-safe graphics code.
+
+## 🎮 Interactions
+
+This demo is designed to be highly interactive:
+- **Move Cursor**: Generates a gravitational pull that attracts the particle field.
+- **Click/Tap**: Triggers a high-velocity particle "burst" at the cursor location.
+- **Hover Boxes**: The interactive neon containers react to focus and mouse proximity.
+- **Backquote (`)**: Toggle the **FConsole** overlay (Note: functionality currently awaiting library update).
+
+## 🛠Tech Stack
+
+- **Core Engine**: [PixiJS v8](https://pixijs.com/)
+- **Debugging**: [@flashist/fconsole](https://github.com/flashist/fconsole)
+- **Framework**: [@flashist/appframework](https://github.com/flashist/appframework)
+- **Build System**: [Vite](https://vitejs.dev/)
+- **Language**: [TypeScript](https://www.typescriptlang.org/)
+
+## 📦 Getting Started
+
+### 1. Installation
+Navigate to this project directory:
+```bash
+cd fexamples/src/modern-pixi8
+```
+Then install the dependencies:
+```bash
+npm install
+```
+
+### 2. Development
+Start the local development server:
+```bash
+npm run dev
+```
+The application will be available at `http://localhost:3000`.
+
+### 3. Build & Production
+To generate a production-ready bundle:
+```bash
+npm run build
+```
+
+---
+
+*Part of the [Flashist Ecosystem](https://github.com/flashist).*
diff --git a/src/modern-pixi8/index.html b/src/modern-pixi8/index.html
new file mode 100644
index 0000000..d282c33
--- /dev/null
+++ b/src/modern-pixi8/index.html
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+ FConsole: Modern Pixi.js v8 Demo
+
+
+
+
+
+
+
+
+
Modern PixiJS v8 Foundation
+
Next-Gen Tooling & Technical Implementation Preview of the Flashist Ecosystem
+
+
+ MOVE to attract • CLICK to burst • HOVER to react
+
+
+
+
+
+
+ Vite • TypeScript • Native v8 WebGL/WebGPU
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modern-pixi8/package.json b/src/modern-pixi8/package.json
new file mode 100644
index 0000000..76f6b35
--- /dev/null
+++ b/src/modern-pixi8/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "modern-pixi8-demo",
+ "version": "0.0.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "pixi.js": "^8.15.0",
+ "@flashist/fconsole": "^0.0.102",
+ "@flashist/flibs": "^0.0.385",
+ "gsap": "^3.12.0",
+ "howler": "^2.2.0",
+ "eventemitter3": "^5.0.0"
+ },
+ "devDependencies": {
+ "typescript": "^5.3.3",
+ "vite": "^5.0.0"
+ }
+}
diff --git a/src/modern-pixi8/src/Game.ts b/src/modern-pixi8/src/Game.ts
new file mode 100644
index 0000000..f426ea6
--- /dev/null
+++ b/src/modern-pixi8/src/Game.ts
@@ -0,0 +1,296 @@
+import { Container, Graphics, Ticker, Point, BlurFilter, Color } from "pixi.js";
+
+interface ParticleData {
+ graphic: Graphics;
+ vx: number;
+ vy: number;
+ life: number;
+ maxLife: number;
+}
+
+/**
+ * Enhanced game scene with physics-based particle field
+ * and 'neon bloom' aesthetics for PixiJS v8.
+ */
+export class Game {
+
+ private _view!: Container;
+ private _particleContainer!: Container;
+ private _sprites: Container[] = [];
+ private _colors: number[] = [0x00ffff, 0xff00ff, 0xffff00, 0x00ff00, 0xff4d4d];
+ private _ticker!: Ticker;
+ private _particles: ParticleData[] = [];
+ private _mousePos: Point = new Point(0, 0);
+ private _isMouseDown: boolean = false;
+
+ constructor() {
+ this._sprites = [];
+ this.construction();
+ this.init();
+ }
+
+ private construction(): void {
+ this._view = new Container();
+ this._view.label = "GameContainer";
+
+ this._particleContainer = new Container();
+ this._particleContainer.label = "ParticleContainer";
+ // Apply a subtle bloom-like effect using blur and additive blending
+ const blur = new BlurFilter({ strength: 2 });
+ this._particleContainer.filters = [blur];
+ // this._particleContainer.blendMode = "add"; // Standard ADD blending for 'glow'
+ }
+
+ private init(): void {
+ this.createBackground();
+ this._view.addChild(this._particleContainer);
+ this.createSprites();
+ this.setupInteraction();
+
+ // Animation ticker
+ this._ticker = new Ticker();
+ this._ticker.add(this.onTick, this);
+ this._ticker.start();
+ }
+
+ private createBackground(): void {
+ const bg: Graphics = new Graphics();
+ bg.label = "Background";
+
+ // Fill background with a deep space gradient-like color
+ bg.rect(0, 0, window.innerWidth, window.innerHeight);
+ bg.fill(0x0a0a1a);
+
+ // Add subtle reactive grid
+ this.drawGrid(bg);
+
+ this._view.addChild(bg);
+ }
+
+ private drawGrid(bg: Graphics): void {
+ bg.stroke({ width: 1, color: 0x1a1a3a });
+ for (let x: number = 0; x <= window.innerWidth; x += 60) {
+ bg.moveTo(x, 0);
+ bg.lineTo(x, window.innerHeight);
+ }
+ for (let y: number = 0; y <= window.innerHeight; y += 60) {
+ bg.moveTo(0, y);
+ bg.lineTo(window.innerWidth, y);
+ }
+ }
+
+ private createSprites(): void {
+ const centerX: number = window.innerWidth / 2;
+ const centerY: number = window.innerHeight / 2;
+
+ for (let i: number = 0; i < 5; i++) {
+ const sprite: Container = this.createColoredBox(
+ this._colors[i],
+ `Box${i + 1}`
+ );
+
+ sprite.x = centerX + (i * 120 - 240);
+ sprite.y = centerY;
+
+ this._sprites.push(sprite);
+ this._view.addChild(sprite);
+ }
+ }
+
+ private createColoredBox(color: number, name: string): Container {
+ const container: Container = new Container();
+ container.label = name;
+
+ const graphics: Graphics = new Graphics();
+ graphics.label = `${name}Graphics`;
+
+ // Draw box with a slight 'glow' via multiple strokes
+ graphics.rect(-40, -40, 80, 80);
+ graphics.fill({ color, alpha: 0.8 });
+ graphics.stroke({ width: 2, color: 0xffffff, alpha: 0.5 });
+ graphics.stroke({ width: 6, color, alpha: 0.2 });
+
+ container.addChild(graphics);
+
+ // Make interactive
+ container.eventMode = "static";
+ container.cursor = "pointer";
+
+ container.on("pointerover", () => {
+ graphics.scale.set(1.1);
+ });
+ container.on("pointerout", () => {
+ graphics.scale.set(1.0);
+ });
+
+ return container;
+ }
+
+ private setupInteraction(): void {
+ const interactionLayer: Graphics = new Graphics();
+ interactionLayer.label = "InteractionLayer";
+ interactionLayer.rect(0, 0, window.innerWidth, window.innerHeight);
+ interactionLayer.fill({ color: 0x000000, alpha: 0 });
+ interactionLayer.eventMode = "static";
+ interactionLayer.cursor = "crosshair";
+
+ interactionLayer.on("pointermove", (event) => {
+ this._mousePos.copyFrom(event.global);
+ });
+
+ interactionLayer.on("pointerdown", () => {
+ this._isMouseDown = true;
+ });
+
+ interactionLayer.on("pointerup", () => {
+ this._isMouseDown = false;
+ });
+
+ interactionLayer.on("pointerupoutside", () => {
+ this._isMouseDown = false;
+ });
+
+ this._view.addChildAt(interactionLayer, 1);
+ }
+
+ private spawnParticle(x: number, y: number, isExplosion: boolean = false): void {
+ const color: number = this._colors[Math.floor(Math.random() * this._colors.length)];
+ const graphic = new Graphics();
+
+ const size = isExplosion ? 2 + Math.random() * 4 : 1 + Math.random() * 2;
+ graphic.circle(0, 0, size);
+ graphic.fill(color);
+ // graphic.blendMode = "add";
+
+ graphic.x = x;
+ graphic.y = y;
+
+ let vx, vy;
+ if (isExplosion) {
+ const angle = Math.random() * Math.PI * 2;
+ const speed = 2 + Math.random() * 5;
+ vx = Math.cos(angle) * speed;
+ vy = Math.sin(angle) * speed;
+ } else {
+ vx = (Math.random() - 0.5) * 2;
+ vy = (Math.random() - 0.5) * 2;
+ }
+
+ const particle: ParticleData = {
+ graphic,
+ vx,
+ vy,
+ life: 1.0,
+ maxLife: 0.01 + Math.random() * 0.02
+ };
+
+ this._particles.push(particle);
+ this._particleContainer.addChild(graphic);
+ }
+
+ private onTick(): void {
+ const time: number = Date.now() * 0.001;
+ const centerY: number = window.innerHeight / 2;
+
+ // Animate boxes
+ for (let i: number = 0; i < this._sprites.length; i++) {
+ const sprite: Container = this._sprites[i];
+ sprite.y = centerY + Math.sin(time * 0.8 + i) * 60;
+ sprite.rotation = Math.sin(time * 0.4 + i) * 0.15;
+
+ // Interaction with mouse
+ const dx = this._mousePos.x - sprite.x;
+ const dy = this._mousePos.y - sprite.y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 200) {
+ const force = (200 - dist) / 200;
+ sprite.x -= dx * force * 0.02;
+ sprite.y -= dy * force * 0.02;
+ }
+ }
+
+ // Spawn particles at mouse position
+ if (this._isMouseDown || Math.random() > 0.5) {
+ const count = this._isMouseDown ? 5 : 1;
+ for (let i = 0; i < count; i++) {
+ this.spawnParticle(
+ this._mousePos.x + (Math.random() - 0.5) * 20,
+ this._mousePos.y + (Math.random() - 0.5) * 20,
+ this._isMouseDown
+ );
+ }
+ }
+
+ // Update particles
+ for (let i = this._particles.length - 1; i >= 0; i--) {
+ const p = this._particles[i];
+
+ // Physics: Attract to mouse
+ const dx = this._mousePos.x - p.graphic.x;
+ const dy = this._mousePos.y - p.graphic.y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+
+ if (dist < 300) {
+ const force = (300 - dist) / 3000;
+ p.vx += dx * force;
+ p.vy += dy * force;
+ }
+
+ // Friction
+ p.vx *= 0.98;
+ p.vy *= 0.98;
+
+ p.graphic.x += p.vx;
+ p.graphic.y += p.vy;
+
+ // Life
+ p.life -= p.maxLife;
+ p.graphic.alpha = p.life;
+ p.graphic.scale.set(p.life);
+
+ if (p.life <= 0) {
+ this._particleContainer.removeChild(p.graphic);
+ p.graphic.destroy();
+ this._particles.splice(i, 1);
+ }
+ }
+
+ // Constrain particle count for performance
+ if (this._particles.length > 1000) {
+ const removed = this._particles.splice(0, this._particles.length - 1000);
+ removed.forEach(p => {
+ this._particleContainer.removeChild(p.graphic);
+ p.graphic.destroy();
+ });
+ }
+ }
+
+ public onResize(): void {
+ const bg = this._view.children.find(c => c.label === "Background") as Graphics;
+ if (bg) {
+ bg.clear();
+ bg.rect(0, 0, window.innerWidth, window.innerHeight);
+ bg.fill(0x0a0a1a);
+ this.drawGrid(bg);
+ }
+
+ const interactionLayer = this._view.children.find(c => c.label === "InteractionLayer") as Graphics;
+ if (interactionLayer) {
+ interactionLayer.clear();
+ interactionLayer.rect(0, 0, window.innerWidth, window.innerHeight);
+ interactionLayer.fill({ color: 0, alpha: 0 });
+ }
+
+ const centerX: number = window.innerWidth / 2;
+ for (let i: number = 0; i < this._sprites.length; i++) {
+ const sprite: Container = this._sprites[i];
+ sprite.x = centerX + (i * 120 - 240);
+ }
+ }
+
+ get view(): Container {
+ return this._view;
+ }
+
+}
+
diff --git a/src/modern-pixi8/src/main.ts b/src/modern-pixi8/src/main.ts
new file mode 100644
index 0000000..b989299
--- /dev/null
+++ b/src/modern-pixi8/src/main.ts
@@ -0,0 +1,81 @@
+import { Application } from "pixi.js";
+import { FC } from "@flashist/fconsole";
+
+import { Game } from "./Game";
+
+// --- Minimal Adapter Shim for FConsole ---
+class Pixi8Adapter {
+ constructor(private app: Application) { }
+ get ticker() { return this.app.ticker; }
+ get renderer() { return this.app.renderer; }
+ createDisplayObjectContainerWrapper(nativeObject: any) { return nativeObject; }
+}
+// Support for legacy global pattern if needed by FConsole
+(window as any).EngineAdapter = { instance: null };
+
+/**
+ * Entry point for the modern Pixi.js v8 demo.
+ * Initializes FConsole with raw Pixi stage.
+ */
+class Main {
+
+ private _app!: Application;
+ private _game!: Game;
+
+ constructor() {
+ this.init();
+ }
+
+ private async init(): Promise {
+ this._app = new Application();
+
+ await this._app.init({
+ resizeTo: window,
+ backgroundColor: 0x0a0a1a,
+ antialias: true
+ });
+
+ document.body.appendChild(this._app.canvas);
+
+ //
+ this._game = new Game();
+ this._app.stage.addChild(this._game.view);
+
+ // Initialize fconsole with raw Pixi stage + shim
+ try {
+ // Set up the global adapter instance for FConsole
+ (window as any).EngineAdapter.instance = new Pixi8Adapter(this._app);
+
+ // Shim the stage with required properties for direct access
+ const stageShim: any = this._app.stage;
+ stageShim.ticker = this._app.ticker;
+ stageShim.renderer = this._app.renderer;
+
+ // @ts-ignore
+ FC.startInit(stageShim);
+ FC.visible = true;
+ } catch (e) {
+ console.warn("FConsole initialization failed:", e);
+ }
+
+ // Handle window resize
+ window.addEventListener("resize", () => this.onResize());
+
+ // Handle FConsole toggle
+ window.addEventListener("keydown", (e) => {
+ if (e.code === "Backquote") {
+ FC.visible = !FC.visible;
+ }
+ });
+ }
+
+ private onResize(): void {
+ if (this._game) {
+ this._game.onResize();
+ }
+ }
+
+}
+
+// Start the application
+new Main();
diff --git a/src/modern-pixi8/tsconfig.json b/src/modern-pixi8/tsconfig.json
new file mode 100644
index 0000000..f402e3f
--- /dev/null
+++ b/src/modern-pixi8/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "lib": ["ES2020", "DOM"],
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/src/modern-pixi8/vite.config.ts b/src/modern-pixi8/vite.config.ts
new file mode 100644
index 0000000..8455c12
--- /dev/null
+++ b/src/modern-pixi8/vite.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ server: {
+ port: 3000,
+ open: true
+ },
+ build: {
+ outDir: 'dist',
+ sourcemap: true
+ },
+ optimizeDeps: {
+ include: ['eventemitter3', '@flashist/fcore', '@flashist/flibs', '@flashist/fconsole']
+ }
+});