Skip to content

Ravtsar1/ShaderGraph

Repository files navigation

ShaderGraph

ShaderGraph is an interactive WebGL plotting toolkit built with p5.js. It gives you a draggable and zoomable 2D coordinate grid, then lets you draw equations, highlight regions, render contour lines, open multiple canvases, animate variables, and plug in custom shaders for more advanced visuals such as Mandelbrot-style rendering.

The project is useful for math visualization, simulation sketches, interactive equation demos, and shader experiments.

Features

  • Interactive 2D coordinate grid with mouse drag panning and scroll-wheel zooming.
  • Automatic major and minor grid spacing using readable 1, 2, 5, 10, ... unit steps.
  • Coordinate conversion helpers for moving between grid units, canvas pixels, and p5.js WebGL positions.
  • Implicit 2D equation drawing through FormulaDrawer.
  • Positive and negative region overlays for equations of the form f(x, y) = 0.
  • Contour plotting for scalar fields through ScalarFieldDrawer.
  • Animated and changeable variables through shader uniforms.
  • Multiple independent canvases on one page.
  • Custom shader loading for specialized visuals.
  • Example pages for formulas, contours, multiple canvases, and custom shaders.

Project Structure

ShaderGraph/
|-- .gitattributes
|-- .gitignore
|-- LICENSE
|-- README.md
|-- Examples/
|   |-- README.md
|   |-- Example.html
|   |-- formulaEx.html
|   |-- scalarEx.html
|   `-- multCanvasEx.html
|-- Simulator/
|   |-- Coordinate-Handler/
|   |-- formula-drawer/
|   |-- scalar-field-drawer/
|   |-- mandlebrotShader/
|   |-- styles/
|   `-- assets/
|-- jsconfig.json
|-- package-lock.json
`-- package.json

Quick Start

Install the development dependency first if you want VS Code hover information and p5.js autocomplete:

npm install

The HTML files use absolute paths such as /Simulator/..., so run the project from a local static server instead of opening the HTML files directly.

From the project root:

python -m http.server 8000

Then open:

http://localhost:8000/Examples/formulaEx.html

You can also use another static server, such as VS Code Live Server or npx serve ..

The examples load p5.js from a CDN, so you need an internet connection unless you replace the CDN script tags with local p5.js files.

Development Setup

This repository does not commit node_modules/. The dependency files are:

  • package.json
  • package-lock.json

After cloning or pulling the project, run:

npm install

That recreates node_modules/ locally and installs @types/p5, which is used by VS Code for hover information and autocomplete. The browser examples still load the p5.js runtime from the CDN script tags in each HTML file.

The project also includes:

  • .gitignore to keep generated dependencies, editor folders, logs, and OS metadata out of Git.
  • .gitattributes to keep text files normalized with LF line endings and mark the bundled font as binary.
  • jsconfig.json to connect JavaScript files with the p5 type definitions.

If VS Code does not show p5 hover information after npm install, reload the VS Code window or run TypeScript: Restart TS Server.

Controls

Click inside a canvas to activate it.

Drag with the mouse to pan around the coordinate plane.

Use the mouse wheel to zoom in or out. The zoom keeps the mouse position anchored so navigation feels natural.

When using multiple canvases, each canvas has its own CoordinateHandler, so each one can be moved and zoomed independently.

Basic Usage

Every sketch follows the same general pattern:

  1. Create a p5.js sketch.
  2. Create a CoordinateHandler.
  3. Load the grid shader and any drawers in preload.
  4. Create a WebGL canvas in setup.
  5. Update the coordinate handler and draw your objects in draw.
/** @param {import("p5")} env */
const sketch = (env) => {
  const coorHandler = new CoordinateHandler(env);
  const formula = new FormulaDrawer(env, `return y - x * x;`);

  env.preload = () => {
    coorHandler.load();
    formula.compileShader();
  };

  env.setup = () => {
    const cnv = env.createCanvas(env.windowWidth, env.windowHeight, env.WEBGL);
    cnv.parent("canvas-container");
    coorHandler.setup(cnv);
    env.pixelDensity(1);
  };

  env.mouseWheel = (event) => {
    coorHandler.handleZoom(event);
  };

  env.draw = () => {
    env.clear();
    env.background(0.18 * 255, 0.18 * 255, 0.18 * 255);

    coorHandler.update();
    coorHandler.drawGrid();

    formula.drawOn(coorHandler);

    coorHandler.drawUnitNumbers();
  };
};

new p5(sketch);

Drawing 2D Equations

Use FormulaDrawer to draw implicit equations. The drawer expects a GLSL-style function body that returns a float. The curve is drawn where the value crosses zero.

For example, this draws the parabola y = x^2:

const formula = new FormulaDrawer(env, `return y - x * x;`);

This draws a circle with radius 2:

const circle = new FormulaDrawer(env, `return x * x + y * y - 4.;`);

The variables x and y are available inside the formula. Because the formula is inserted into a shader, use GLSL-style numeric syntax where needed, such as 4. instead of just 4.

Highlighting Regions

FormulaDrawer can color the positive side, negative side, and intersection line separately:

const circle = new FormulaDrawer(env, `return x * x + y * y - 4.;`, {
  colorIntersect: [1.0, 0.9, 0.2, 1.0],
  colorPlus: [1.0, 0.2, 0.2, 0.25],
  colorMinus: [0.2, 0.4, 1.0, 0.25],
});

Color values are RGBA arrays from 0.0 to 1.0.

  • colorIntersect controls the equation line.
  • colorPlus controls the region where f(x, y) > 0.
  • colorMinus controls the region where f(x, y) < 0.

Contour Plots

Use ScalarFieldDrawer to draw contour lines for a scalar field, which is the same idea as plotting level curves of a 3D surface z = f(x, y).

const scalarField = new ScalarFieldDrawer(env, `return sin(x) * cos(y);`, {
  color: [1.0, 0.6, 0.1, 1.0],
  resolution: 5,
});

Higher resolution values create more contour lines per grid unit.

Interactive Variables

Both FormulaDrawer and ScalarFieldDrawer can receive extra variables through addVar. These variables become shader uniforms and can be updated every frame.

const circle = new FormulaDrawer(
  env,
  `return x * x + y * y - pow(radius, 2.);`,
  {
    addVar: ["radius"],
    colorIntersect: [1.0, 0.9, 0.2, 1.0],
    colorPlus: [0, 0, 0, 0],
    colorMinus: [0, 0, 0, 0],
  },
);

Then update it in draw:

circle.update([Math.sin(env.frameCount / 50) + 2]);
circle.drawOn(coorHandler);

The order of values passed to update must match the order of variable names in addVar.

For multiple variables, list each uniform name in addVar, then pass the values in the same order:

const ellipse = new FormulaDrawer(
  env,
  `return pow(x / radiusX, 2.) + pow(y / radiusY, 2.) - 1.;`,
  {
    addVar: ["radiusX", "radiusY"],
    colorIntersect: [0.2, 1.0, 0.8, 1.0],
    colorPlus: [0, 0, 0, 0],
    colorMinus: [0, 0, 0, 0],
  },
);

Then update both values together:

ellipse.update([
  2 + Math.sin(env.frameCount / 60),
  1 + 0.5 * Math.cos(env.frameCount / 80),
]);
ellipse.drawOn(coorHandler);

Custom Shaders

CoordinateHandler.load() can receive a custom shader. This is useful when you want to replace the default grid rendering with a specialized shader effect.

env.preload = () => {
  const shader = env.loadShader(
    "/Simulator/Coordinate-Handler/default.vert",
    "/Simulator/mandlebrotShader/mandlebrot.frag",
  );

  coorHandler.load(shader);
};

The Mandelbrot example uses this pattern.

Examples

See Examples/README.md for a guided explanation of the included examples.

Useful entry points:

  • Examples/Example.html - basic coordinate handling and custom shader loading.
  • Examples/formulaEx.html - implicit equations, highlighted regions, and animated variables.
  • Examples/scalarEx.html - contour plots from scalar fields.
  • Examples/multCanvasEx.html - multiple independent canvases on one page.

Main Classes

CoordinateHandler

Manages the visible coordinate system. It handles panning, zooming, grid drawing, axis labels, coordinate bounds, and coordinate conversion.

Common methods:

  • load(gridShader, font) loads shader and font resources.
  • setup(canvas, centreX, centreY, sideLength, sideLengthRatio, majorThick, minorThick, gridColor) initializes the coordinate system.
  • update() handles activation, dragging, and shader uniforms.
  • handleZoom(event) handles mouse wheel zoom.
  • drawGrid() draws the grid shader.
  • drawUnitNumbers() draws coordinate labels.
  • getMinMaxVal() returns [minx, maxx, miny, maxy].

FormulaDrawer

Draws an implicit 2D formula where f(x, y) = 0.

Common options:

  • colorIntersect for the equation line.
  • colorPlus for the positive region.
  • colorMinus for the negative region.
  • addVar for custom animated variables.
  • funcMode for line rendering mode.

ScalarFieldDrawer

Draws contour lines for scalar fields.

Common options:

  • color for contour color.
  • addVar for custom animated variables.
  • resolution for contour density.

Troubleshooting

If the canvas is blank, check the browser console first. Shader syntax errors usually appear there.

If assets fail to load, make sure you are running the project from a local server at the project root.

If an asset works on Windows but fails after publishing, check path casing. Hosts such as GitHub Pages are case-sensitive, so /Simulator/assets/Inconsolata.otf and /Simulator/assets/inconsolata.otf are different paths.

If a formula does not draw, confirm that it returns a float and follows GLSL-style syntax.

If zooming does not work, click inside the canvas first to activate it.

If performance drops, reduce shader loop counts, lower the number of canvases, simplify formulas, or keep env.pixelDensity(1).

License

This project is licensed under the MIT License. See LICENSE.

The included Inconsolata font uses the SIL Open Font License, included in Simulator/assets/SIL Open Font License.txt.

See THIRD_PARTY_NOTICES.md for third-party asset and dependency notices.

About

Interactive WebGL plotting toolkit for p5.js with draggable grids, zooming, implicit equation plotting, region highlighting, contour plots, animated variables, multiple canvases, and custom shaders. You can draw anything that drawable with p5.js on top of this!

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors