
Infographic Generator P5
- 22 installs
- 2 repo stars
- Updated August 1, 2026
- vishalsachdev/claude-skills
Helps with ai & agent building tasks.
About
infographic-generator-p5 is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- infographic-generator-p5
- AI & Agent Building
- AI-coding skill
Infographic Generator P5 by the numbers
- 22 all-time installs (skills.sh)
- +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #10,123 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vishalsachdev/claude-skills --skill infographic-generator-p5Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 22 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 1, 2026 |
| Repository | vishalsachdev/claude-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Infographic Generator with P5.js
Overview
Generate interactive educational infographics using p5.js that visualize relationships between concepts through nodes and edges. Infographics are data-driven visualizations that read from JSON files in vis-network format, allowing users to hover over elements to see a short definition in a tooltip and detailed information with links below the drawing area.
Purpose
Infographics transform complex concept relationships into visual, explorable diagrams. Unlike MicroSims which focus on simulation and interaction controls, infographics emphasize information display and exploration through hover interactions. The control region is dedicated exclusively to displaying detailed information about the currently hovered item.
When to Use This Skill
Use this skill when:
- Creating concept maps, mind maps, or knowledge graphs
- Visualizing relationships between topics, ideas, or entities
- Building interactive diagrams for educational content
- Converting vis-network data into standalone p5.js visualizations with enhanced behaviors
- Creating hover and click through based information exploration interfaces
When to Avoid This Skill
- When you want a fully responsive design that keeps the items in the center regardless of the container size
- When you have complex diagrams where the edges that connect the concepts must be routed using curves and multiple points in a path description
Key Differences from MicroSim
1. Control Region Purpose: The control area ONLY displays details of the hovered item (no sliders, buttons, or other controls) 2. Data-Driven: Reads node and edge data from data.json file 3. Interaction Model: Primarily hover-based exploration rather than parameter manipulation 4. Layout: Nodes have fixed positions defined in the data file
Development Process
Step 1: Define Infographic Requirements
Gather the following information:
1. Subject Area: What topic does this infographic visualize? 2. Audience Level: Elementary, Middle School, High School, Undergraduate, Graduate 3. Node Types: What categories/groups of nodes exist? 4. Relationships: What do the edges represent? 5. Data Source: Where will the node/edge data come from?
Step 2: Prepare Data File (data.json)
Create a JSON file following the vis-network format with metadata, groups, nodes, and edges:
{
"metadata": {
"title": "Topic Overview",
"description": "An interactive infographic showing relationships between concepts",
"author": "Your Name",
"created": "2025-01-09"
},
"groups": {
"foundation": {
"color": "#FF6B6B",
"borderColor": "#C92A2A",
"shape": "ellipse"
},
"intermediate": {
"color": "#4ECDC4",
"borderColor": "#0B7285",
"shape": "box"
}
},
"nodes": [
{
"id": 1,
"label": "Concept Name",
"group": "foundation",
"x": 100,
"y": 100,
"shortDescription": "Brief one-sentence description for tooltip",
"fullDescription": "Detailed paragraph about this concept. Can include <a href='url'>links</a> and formatting."
}
],
"edges": [
{
"from": 1,
"to": 2,
"label": "depends on",
"color": "#999999",
"width": 2
}
]
}Step 3: Generate Infographic Files
Create the following folder structure in /docs/sims/$INFOGRAPHIC_NAME/:
/docs/sims/$INFOGRAPHIC_NAME/
├── index.md # Documentation page with iframe
├── main.html # HTML file with p5.js CDN
├── $INFOGRAPHIC_NAME.js # p5.js JavaScript code
└── data.json # Node and edge dataTechnical Architecture
Canvas Structure (REQUIRED)
Every infographic must have two regions:
1. Drawing Region (top): Displays nodes, edges, labels, and tooltips 2. Detail Display Region (bottom): Shows full description of hovered item
// Canvas dimensions - REQUIRED structure
let canvasWidth = 800; // Initial width (responsive)
let drawHeight = 600; // Drawing area height
let controlHeight = 120; // Detail display area height
let canvasHeight = drawHeight + controlHeight;
let margin = 20; // Margin for visual elements
let defaultTextSize = 16; // Base text size
// Data variables
let infographicData;
let nodes = [];
let edges = [];
let groups = {};
let hoveredNode = null;
let tooltipData = null;
function preload() {
infographicData = loadJSON('data.json');
}
function setup() {
updateCanvasSize();
const canvas = createCanvas(canvasWidth, canvasHeight);
canvas.parent(document.querySelector('main'));
// Parse data
parseData();
describe('Interactive infographic showing concept relationships', LABEL);
}
function draw() {
updateCanvasSize();
// Drawing area background
fill('aliceblue');
rect(0, 0, width, drawHeight);
// Detail display area background
fill('white');
rect(0, drawHeight, width, controlHeight);
// Draw edges first (behind nodes)
drawEdges();
// Draw nodes
drawNodes();
// Draw tooltip if hovering over a node
drawTooltip();
// Draw detail panel for hovered node
drawDetailPanel();
}Data Parsing
function parseData() {
// Parse groups
if (infographicData.groups) {
groups = infographicData.groups;
}
// Parse nodes
nodes = infographicData.nodes.map(n => ({
id: n.id,
label: n.label || '',
x: n.x || 0,
y: n.y || 0,
group: n.group || 'default',
shortDescription: n.shortDescription || '',
fullDescription: n.fullDescription || '',
shape: n.shape || (groups[n.group]?.shape || 'ellipse'),
color: n.color || (groups[n.group]?.color || '#97C2FC'),
borderColor: n.borderColor || (groups[n.group]?.borderColor || '#2B7CE9'),
borderWidth: n.borderWidth || 2,
size: n.size || 40,
icon: n.icon || null
}));
// Parse edges
edges = infographicData.edges.map(e => ({
from: e.from,
to: e.to,
label: e.label || '',
color: e.color || '#848484',
width: e.width || 1,
dashes: e.dashes || false,
smooth: e.smooth || { type: 'continuous', roundness: 0.5 }
}));
}Node Drawing
function drawNodes() {
hoveredNode = null;
for (let node of nodes) {
// Check if mouse is hovering over this node
let d = dist(mouseX, mouseY, node.x, node.y);
let isHovered = d < node.size;
if (isHovered && mouseY < drawHeight) {
hoveredNode = node;
}
// Draw node shape
push();
stroke(node.borderColor);
strokeWeight(isHovered ? node.borderWidth + 1 : node.borderWidth);
fill(node.color);
if (node.shape === 'ellipse' || node.shape === 'circle') {
ellipse(node.x, node.y, node.size * 2);
} else if (node.shape === 'box' || node.shape === 'square') {
rectMode(CENTER);
rect(node.x, node.y, node.size * 1.8, node.size * 1.8);
} else if (node.shape === 'diamond') {
drawDiamond(node.x, node.y, node.size);
}
// Draw icon if available
if (node.icon) {
// Icon drawing code here
}
pop();
// Draw label
fill(0);
noStroke();
textAlign(CENTER, CENTER);
textSize(14);
text(node.label, node.x, node.y + node.size + 15);
}
}
function drawDiamond(x, y, size) {
beginShape();
vertex(x, y - size); // top
vertex(x + size, y); // right
vertex(x, y + size); // bottom
vertex(x - size, y); // left
endShape(CLOSE);
}Edge Drawing
function drawEdges() {
for (let edge of edges) {
let fromNode = nodes.find(n => n.id === edge.from);
let toNode = nodes.find(n => n.id === edge.to);
if (!fromNode || !toNode) continue;
push();
stroke(edge.color);
strokeWeight(edge.width);
if (edge.dashes) {
drawingContext.setLineDash([5, 5]);
}
// Draw curved or straight line based on smooth parameter
if (edge.smooth && edge.smooth.type === 'curvedCW') {
// Draw curved line clockwise
noFill();
let controlX = (fromNode.x + toNode.x) / 2 + 50;
let controlY = (fromNode.y + toNode.y) / 2;
bezier(fromNode.x, fromNode.y, controlX, controlY,
controlX, controlY, toNode.x, toNode.y);
} else {
// Draw straight line
line(fromNode.x, fromNode.y, toNode.x, toNode.y);
}
drawingContext.setLineDash([]);
pop();
// Draw edge label if exists
if (edge.label) {
let midX = (fromNode.x + toNode.x) / 2;
let midY = (fromNode.y + toNode.y) / 2;
fill(100);
noStroke();
textAlign(CENTER, CENTER);
textSize(12);
text(edge.label, midX, midY);
}
}
}Tooltip Display (REQUIRED)
Tooltips must always remain visible within the drawing area, even when hovering near edges:
function drawTooltip() {
if (!hoveredNode || mouseY >= drawHeight) return;
let tooltipText = hoveredNode.shortDescription;
if (!tooltipText) return;
// Measure tooltip dimensions
textSize(14);
let tooltipWidth = textWidth(tooltipText) + 20;
let tooltipHeight = 30;
let padding = 10;
// Position tooltip near mouse, but keep within drawing bounds
let tooltipX = mouseX + 15;
let tooltipY = mouseY - 20;
// Adjust if tooltip would go off right edge
if (tooltipX + tooltipWidth > width - padding) {
tooltipX = mouseX - tooltipWidth - 15;
}
// Adjust if tooltip would go off left edge
if (tooltipX < padding) {
tooltipX = padding;
}
// Adjust if tooltip would go off top edge
if (tooltipY < padding) {
tooltipY = mouseY + 20;
}
// Adjust if tooltip would go off bottom of drawing area
if (tooltipY + tooltipHeight > drawHeight - padding) {
tooltipY = drawHeight - tooltipHeight - padding;
}
// Draw tooltip background
push();
fill(255, 255, 220);
stroke(150);
strokeWeight(1);
rect(tooltipX, tooltipY, tooltipWidth, tooltipHeight, 5);
// Draw tooltip text
fill(0);
noStroke();
textAlign(LEFT, CENTER);
textSize(14);
text(tooltipText, tooltipX + 10, tooltipY + tooltipHeight / 2);
pop();
}Detail Panel Display (REQUIRED)
The control region exclusively displays the full description of the hovered item:
function drawDetailPanel() {
push();
fill(0);
noStroke();
textAlign(LEFT, TOP);
textSize(defaultTextSize);
let panelX = margin;
let panelY = drawHeight + 10;
let panelWidth = width - 2 * margin;
if (hoveredNode) {
// Display node label as header
textSize(18);
textStyle(BOLD);
text(hoveredNode.label, panelX, panelY);
// Display full description
textSize(defaultTextSize);
textStyle(NORMAL);
// Note: p5.js doesn't render HTML, so strip tags for display
let displayText = hoveredNode.fullDescription.replace(/<[^>]*>/g, '');
// Wrap text to fit panel width
let words = displayText.split(' ');
let line = '';
let y = panelY + 25;
for (let word of words) {
let testLine = line + word + ' ';
if (textWidth(testLine) > panelWidth && line.length > 0) {
text(line, panelX, y);
line = word + ' ';
y += 20;
} else {
line = testLine;
}
}
text(line, panelX, y);
} else {
// Default message when nothing is hovered
textStyle(ITALIC);
fill(100);
text('Hover over a concept to see details...', panelX, panelY);
}
pop();
}Responsive Design (REQUIRED)
function windowResized() {
updateCanvasSize();
resizeCanvas(canvasWidth, canvasHeight);
}
function updateCanvasSize() {
const container = document.querySelector('main');
if (container) {
canvasWidth = container.offsetWidth;
}
}File Templates
main.html Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Infographic Title</title>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.10/lib/p5.js"></script>
<style>
body {
margin: 0px;
padding: 0px;
font-family: Arial, Helvetica, sans-serif;
}
</style>
<script src="infographic-name.js"></script>
</head>
<body>
<main></main>
<br/>
<a href=".">Back to Documentation</a>
</body>
</html>index.md Template
---
title: Infographic Title
description: Brief description of the infographic content
---
# Infographic Title
<iframe src="main.html" height="722px" scrolling="no"></iframe>
[View Fullscreen](./main.html){ .md-button .md-button--primary }
## Description
[Description of what this infographic visualizes]
## How to Use
1. **Hover** over any concept to see a brief description
2. **Read** the detailed information in the panel below the diagram
3. **Explore** the relationships shown by the connecting lines
## Legend
[Explanation of colors, shapes, and edge types]Data Format Specification
Node Properties
Each node in the nodes array supports these properties:
- id (required): Unique integer identifier
- label (required): Display text (max 30 characters recommended)
- x (required): Horizontal position in pixels
- y (required): Vertical position in pixels
- shortDescription (required): One-sentence tooltip text (no HTML)
- fullDescription (required): Detailed paragraph (can include HTML links)
- group (optional): Group identifier for styling
- shape (optional): 'ellipse', 'box', 'diamond', 'circle', 'square'
- color (optional): Fill color (hex or named color)
- borderColor (optional): Border color
- borderWidth (optional): Border thickness in pixels
- size (optional): Node radius/size in pixels
- icon (optional): Icon identifier (future use)
Edge Properties
Each edge in the edges array supports these properties:
- from (required): Source node ID
- to (required): Target node ID
- label (optional): Text label for the edge
- color (optional): Edge color (default: '#999999')
- width (optional): Line thickness (default: 1)
- dashes (optional): Boolean for dashed line (default: false)
- smooth (optional): Object with type and roundness for curved edges
Group Properties
Groups define default styling for node categories:
- color: Default fill color for nodes in this group
- borderColor: Default border color
- shape: Default shape type
Quality Standards
Every infographic must meet these criteria:
- ✅ Loads and parses data.json without errors
- ✅ All nodes are visible and properly positioned
- ✅ Tooltips remain within drawing bounds
- ✅ Detail panel displays full descriptions correctly
- ✅ Responsive design adapts to container width
- ✅ Clear visual hierarchy and readable labels
- ✅ Edges don't obscure important information
Deployment
After generating the infographic files:
1. Place the folder in /docs/sims/$INFOGRAPHIC_NAME/ 2. Update mkdocs.yml navigation to include the new infographic 3. Test locally with mkdocs serve 4. Deploy with mkdocs gh-deploy
Common Use Cases
Concept Maps
Show relationships between educational concepts with hierarchical or network structures.
Timeline Visualizations
Display historical events or process steps as connected nodes.
System Diagrams
Visualize components and their relationships in complex systems.
Knowledge Graphs
Create explorable networks of related topics or ideas.
Areas for Extension
- make the design responsive
- allow items to be placed relative to other items (above, below, right-of, left-of)
- create animated lines between items to show flows
References
This skill uses vis-network compatible data formats. For detailed parameter documentation, see references/vis-network-parameters.md.
Template files are available in assets/ for quick start:
assets/template-main.htmlassets/template-infographic.jsassets/template-data.json
{
"metadata": {
"title": "Sample Concept Map",
"description": "An interactive infographic demonstrating the infograph-generator-p5 template",
"author": "Template Author",
"created": "2025-01-09",
"subject": "Educational Technology",
"version": "1.0"
},
"groups": {
"foundation": {
"color": "#FF6B6B",
"borderColor": "#C92A2A",
"shape": "ellipse"
},
"intermediate": {
"color": "#4ECDC4",
"borderColor": "#0B7285",
"shape": "box"
},
"advanced": {
"color": "#95E1D3",
"borderColor": "#087F5B",
"shape": "diamond"
}
},
"nodes": [
{
"id": 1,
"label": "Core Concept",
"group": "foundation",
"x": 400,
"y": 100,
"shortDescription": "The foundational idea upon which everything builds",
"fullDescription": "This is the core concept that serves as the foundation for all other related ideas. Understanding this concept is essential before moving to more advanced topics. It represents the most basic principles that students must grasp first."
},
{
"id": 2,
"label": "Supporting Idea A",
"group": "intermediate",
"x": 200,
"y": 250,
"shortDescription": "First major supporting concept",
"fullDescription": "This supporting idea builds directly on the core concept and represents an important application or extension of the foundational principles. Students should understand the core concept before studying this topic."
},
{
"id": 3,
"label": "Supporting Idea B",
"group": "intermediate",
"x": 600,
"y": 250,
"shortDescription": "Second major supporting concept",
"fullDescription": "Another important supporting idea that also builds on the core concept. This represents a different application or perspective compared to Supporting Idea A, showing the versatility of the foundational principles."
},
{
"id": 4,
"label": "Advanced Topic 1",
"group": "advanced",
"x": 150,
"y": 450,
"shortDescription": "Complex topic requiring multiple prerequisites",
"fullDescription": "This advanced topic combines understanding from both the core concept and Supporting Idea A. It represents a more sophisticated application that requires synthesis of multiple prerequisite ideas. Students should master the foundational concepts first."
},
{
"id": 5,
"label": "Advanced Topic 2",
"group": "advanced",
"x": 400,
"y": 450,
"shortDescription": "Integration of multiple concepts",
"fullDescription": "An advanced topic that integrates knowledge from both supporting ideas. This demonstrates how different paths of learning can converge to enable understanding of more complex concepts. Requires solid foundation in prerequisites."
},
{
"id": 6,
"label": "Advanced Topic 3",
"group": "advanced",
"x": 650,
"y": 450,
"shortDescription": "Highest level of conceptual complexity",
"fullDescription": "The most advanced topic in this concept map, building primarily on Supporting Idea B but also requiring understanding of the core concept. Represents the pinnacle of learning in this domain and requires mastery of all prerequisite concepts."
}
],
"edges": [
{
"from": 1,
"to": 2,
"label": "builds on",
"color": "#999999",
"width": 2
},
{
"from": 1,
"to": 3,
"label": "builds on",
"color": "#999999",
"width": 2
},
{
"from": 2,
"to": 4,
"label": "leads to",
"color": "#666666",
"width": 1.5
},
{
"from": 2,
"to": 5,
"label": "contributes",
"color": "#666666",
"width": 1.5
},
{
"from": 3,
"to": 5,
"label": "contributes",
"color": "#666666",
"width": 1.5
},
{
"from": 3,
"to": 6,
"label": "leads to",
"color": "#666666",
"width": 1.5
},
{
"from": 1,
"to": 5,
"label": "informs",
"color": "#AAAAAA",
"width": 1,
"dashes": true
}
]
}
// Interactive Infographic Template
// This template demonstrates the core structure for creating
// data-driven infographics with hover interactions
// Canvas dimensions - REQUIRED structure
let canvasWidth = 800; // Initial width (responsive)
let drawHeight = 600; // Drawing area height
let controlHeight = 120; // Detail display area height
let canvasHeight = drawHeight + controlHeight;
let margin = 20; // Margin for visual elements
let defaultTextSize = 16; // Base text size
// Data variables
let infographicData;
let nodes = [];
let edges = [];
let groups = {};
let hoveredNode = null;
function preload() {
infographicData = loadJSON('data.json');
}
function setup() {
updateCanvasSize();
const canvas = createCanvas(canvasWidth, canvasHeight);
canvas.parent(document.querySelector('main'));
// Parse data from JSON
parseData();
describe('Interactive infographic showing concept relationships', LABEL);
}
function draw() {
updateCanvasSize();
// Drawing area background
fill('aliceblue');
rect(0, 0, width, drawHeight);
// Detail display area background
fill('white');
rect(0, drawHeight, width, controlHeight);
// Draw edges first (behind nodes)
drawEdges();
// Draw nodes
drawNodes();
// Draw tooltip if hovering over a node
drawTooltip();
// Draw detail panel for hovered node
drawDetailPanel();
}
function parseData() {
// Parse groups
if (infographicData.groups) {
groups = infographicData.groups;
}
// Parse nodes
nodes = infographicData.nodes.map(n => ({
id: n.id,
label: n.label || '',
x: n.x || 0,
y: n.y || 0,
group: n.group || 'default',
shortDescription: n.shortDescription || '',
fullDescription: n.fullDescription || '',
shape: n.shape || (groups[n.group]?.shape || 'ellipse'),
color: n.color || (groups[n.group]?.color || '#97C2FC'),
borderColor: n.borderColor || (groups[n.group]?.borderColor || '#2B7CE9'),
borderWidth: n.borderWidth || 2,
size: n.size || 40,
icon: n.icon || null
}));
// Parse edges
edges = infographicData.edges.map(e => ({
from: e.from,
to: e.to,
label: e.label || '',
color: e.color || '#848484',
width: e.width || 1,
dashes: e.dashes || false,
smooth: e.smooth || { type: 'continuous', roundness: 0.5 }
}));
}
function drawNodes() {
hoveredNode = null;
for (let node of nodes) {
// Check if mouse is hovering over this node
let d = dist(mouseX, mouseY, node.x, node.y);
let isHovered = d < node.size;
if (isHovered && mouseY < drawHeight) {
hoveredNode = node;
}
// Draw node shape
push();
stroke(node.borderColor);
strokeWeight(isHovered ? node.borderWidth + 1 : node.borderWidth);
fill(node.color);
if (node.shape === 'ellipse' || node.shape === 'circle') {
ellipse(node.x, node.y, node.size * 2);
} else if (node.shape === 'box' || node.shape === 'square') {
rectMode(CENTER);
rect(node.x, node.y, node.size * 1.8, node.size * 1.8);
} else if (node.shape === 'diamond') {
drawDiamond(node.x, node.y, node.size);
} else if (node.shape === 'triangle') {
drawTriangle(node.x, node.y, node.size);
}
pop();
// Draw label below node
fill(0);
noStroke();
textAlign(CENTER, CENTER);
textSize(14);
text(node.label, node.x, node.y + node.size + 15);
}
}
function drawDiamond(x, y, size) {
beginShape();
vertex(x, y - size); // top
vertex(x + size, y); // right
vertex(x, y + size); // bottom
vertex(x - size, y); // left
endShape(CLOSE);
}
function drawTriangle(x, y, size) {
beginShape();
vertex(x, y - size); // top
vertex(x + size * 0.866, y + size/2); // bottom right
vertex(x - size * 0.866, y + size/2); // bottom left
endShape(CLOSE);
}
function drawEdges() {
for (let edge of edges) {
let fromNode = nodes.find(n => n.id === edge.from);
let toNode = nodes.find(n => n.id === edge.to);
if (!fromNode || !toNode) continue;
push();
stroke(edge.color);
strokeWeight(edge.width);
if (edge.dashes) {
drawingContext.setLineDash([5, 5]);
}
// Draw curved or straight line based on smooth parameter
if (edge.smooth && edge.smooth.type === 'curvedCW') {
// Draw curved line clockwise
noFill();
let controlX = (fromNode.x + toNode.x) / 2 + 50;
let controlY = (fromNode.y + toNode.y) / 2;
bezier(fromNode.x, fromNode.y, controlX, controlY,
controlX, controlY, toNode.x, toNode.y);
} else if (edge.smooth && edge.smooth.type === 'curvedCCW') {
// Draw curved line counter-clockwise
noFill();
let controlX = (fromNode.x + toNode.x) / 2 - 50;
let controlY = (fromNode.y + toNode.y) / 2;
bezier(fromNode.x, fromNode.y, controlX, controlY,
controlX, controlY, toNode.x, toNode.y);
} else {
// Draw straight line
line(fromNode.x, fromNode.y, toNode.x, toNode.y);
}
drawingContext.setLineDash([]);
pop();
// Draw edge label if exists
if (edge.label) {
let midX = (fromNode.x + toNode.x) / 2;
let midY = (fromNode.y + toNode.y) / 2;
// Background for label readability
push();
fill(255, 255, 255, 200);
noStroke();
textSize(12);
let labelWidth = textWidth(edge.label) + 8;
rectMode(CENTER);
rect(midX, midY, labelWidth, 18, 3);
pop();
// Label text
fill(100);
noStroke();
textAlign(CENTER, CENTER);
textSize(12);
text(edge.label, midX, midY);
}
}
}
function drawTooltip() {
if (!hoveredNode || mouseY >= drawHeight) return;
let tooltipText = hoveredNode.shortDescription;
if (!tooltipText) return;
// Measure tooltip dimensions
textSize(14);
let tooltipWidth = textWidth(tooltipText) + 20;
let tooltipHeight = 30;
let padding = 10;
// Position tooltip near mouse, but keep within drawing bounds
let tooltipX = mouseX + 15;
let tooltipY = mouseY - 20;
// Adjust if tooltip would go off right edge
if (tooltipX + tooltipWidth > width - padding) {
tooltipX = mouseX - tooltipWidth - 15;
}
// Adjust if tooltip would go off left edge
if (tooltipX < padding) {
tooltipX = padding;
}
// Adjust if tooltip would go off top edge
if (tooltipY < padding) {
tooltipY = mouseY + 20;
}
// Adjust if tooltip would go off bottom of drawing area
if (tooltipY + tooltipHeight > drawHeight - padding) {
tooltipY = drawHeight - tooltipHeight - padding;
}
// Draw tooltip background
push();
fill(255, 255, 220);
stroke(150);
strokeWeight(1);
rect(tooltipX, tooltipY, tooltipWidth, tooltipHeight, 5);
// Draw tooltip text
fill(0);
noStroke();
textAlign(LEFT, CENTER);
textSize(14);
text(tooltipText, tooltipX + 10, tooltipY + tooltipHeight / 2);
pop();
}
function drawDetailPanel() {
push();
fill(0);
noStroke();
textAlign(LEFT, TOP);
textSize(defaultTextSize);
let panelX = margin;
let panelY = drawHeight + 10;
let panelWidth = width - 2 * margin;
if (hoveredNode) {
// Display node label as header
textSize(18);
textStyle(BOLD);
text(hoveredNode.label, panelX, panelY);
// Display full description
textSize(defaultTextSize);
textStyle(NORMAL);
// Note: p5.js doesn't render HTML, so strip tags for display
let displayText = hoveredNode.fullDescription.replace(/<[^>]*>/g, '');
// Wrap text to fit panel width
let words = displayText.split(' ');
let line = '';
let y = panelY + 25;
for (let word of words) {
let testLine = line + word + ' ';
if (textWidth(testLine) > panelWidth && line.length > 0) {
text(line, panelX, y);
line = word + ' ';
y += 20;
// Stop if we run out of space in the control panel
if (y > drawHeight + controlHeight - 10) break;
} else {
line = testLine;
}
}
if (y <= drawHeight + controlHeight - 10) {
text(line, panelX, y);
}
} else {
// Default message when nothing is hovered
textStyle(ITALIC);
fill(100);
text('Hover over a concept to see details...', panelX, panelY);
}
pop();
}
function windowResized() {
updateCanvasSize();
resizeCanvas(canvasWidth, canvasHeight);
}
function updateCanvasSize() {
const container = document.querySelector('main');
if (container) {
canvasWidth = container.offsetWidth;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Interactive Infographic using P5.js 1.11.10</title>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.10/lib/p5.js"></script>
<style>
body {
margin: 0px;
padding: 0px;
font-family: Arial, Helvetica, sans-serif;
}
</style>
<!-- Replace with your infographic JavaScript file name -->
<script src="concept-map.js"></script>
</head>
<body>
<main></main>
<br/>
<a href=".">Back to Documentation</a>
</body>
</html>
Vis-Network Data Format Reference
This document provides comprehensive reference documentation for the vis-network compatible data format used by the infographic-generator-p5 skill. The skill reads these files to place items with hovers on the drawing area of the screen.
Overview
The infographic-generator-p5 skill reads data from a data.json file that follows the vis-network library format. This format is widely used for network visualizations and ensures compatibility with other visualization tools.
JSON Structure
The data file has four main sections:
{
"metadata": { ... },
"groups": { ... },
"nodes": [ ... ],
"edges": [ ... ]
}Metadata Section
Optional metadata about the infographic:
"metadata": {
"title": "string - Title of the infographic",
"description": "string - Brief description",
"author": "string - Creator name",
"created": "string - Creation date (YYYY-MM-DD)",
"subject": "string - Subject area or topic",
"version": "string - Version number"
}Groups Section
Groups define default styling for categories of nodes. Each group has a unique identifier as the key:
"groups": {
"group-id": {
"color": "string - Fill color (hex or named)",
"borderColor": "string - Border color (hex or named)",
"shape": "string - Default shape type"
}
}Supported Group Properties
| Property | Type | Description | Example |
|---|---|---|---|
| color | string | Node fill color | "#FF6B6B" or "red" |
| borderColor | string | Node border color | "#C92A2A" or "darkred" |
| shape | string | Node shape type | "ellipse", "box", "diamond" |
Example Groups
"groups": {
"foundation": {
"color": "#FF6B6B",
"borderColor": "#C92A2A",
"shape": "ellipse"
},
"intermediate": {
"color": "#4ECDC4",
"borderColor": "#0B7285",
"shape": "box"
},
"advanced": {
"color": "#95E1D3",
"borderColor": "#087F5B",
"shape": "diamond"
}
}Nodes Section
Nodes represent individual concepts or entities in the infographic. Each node is an object in the nodes array.
Required Node Properties
| Property | Type | Description | Example |
|---|---|---|---|
| id | number | Unique identifier | 1 |
| label | string | Display text (max 30 chars) | "Core Concept" |
| x | number | X position in pixels | 400 |
| y | number | Y position in pixels | 100 |
| shortDescription | string | Tooltip text (one sentence) | "Brief description" |
| fullDescription | string | Detail panel text (paragraph) | "Full explanation..." |
Optional Node Properties
| Property | Type | Default | Description | Example |
|---|---|---|---|---|
| group | string | "default" | Group identifier for styling | "foundation" |
| shape | string | "ellipse" | Node shape | "box", "diamond", "circle" |
| color | string | group default | Fill color | "#97C2FC" |
| borderColor | string | group default | Border color | "#2B7CE9" |
| borderWidth | number | 2 | Border thickness | 3 |
| size | number | 40 | Node radius/size | 50 |
| icon | string | null | Icon identifier (future) | "book" |
Supported Node Shapes
- ellipse - Oval/circular shape (default)
- circle - Perfect circle
- box - Rectangle/square
- square - Perfect square
- diamond - Four-pointed diamond
- triangle - Equilateral triangle pointing up
Example Node
{
"id": 1,
"label": "Core Concept",
"group": "foundation",
"x": 400,
"y": 100,
"size": 45,
"shape": "ellipse",
"color": "#FF6B6B",
"borderColor": "#C92A2A",
"borderWidth": 2,
"shortDescription": "The foundational idea upon which everything builds",
"fullDescription": "This is the core concept that serves as the foundation for all other related ideas. Understanding this concept is essential before moving to more advanced topics."
}Node Description Guidelines
shortDescription (for tooltips):
- Single sentence
- 40-80 characters recommended
- No HTML formatting
- Concise and informative
- Example: "The foundational principle of the system"
fullDescription (for detail panel):
- 1-3 paragraphs
- Can include HTML links:
<a href='url'>text</a> - Explain significance and relationships
- Provide context and learning guidance
- Example: "This concept represents the fundamental principle that... It connects to other ideas by... Students should understand..."
Edges Section
Edges represent relationships or connections between nodes. Each edge is an object in the edges array.
Required Edge Properties
| Property | Type | Description | Example |
|---|---|---|---|
| from | number | Source node ID | 1 |
| to | number | Target node ID | 2 |
Optional Edge Properties
| Property | Type | Default | Description | Example |
|---|---|---|---|---|
| label | string | "" | Text label for edge | "builds on" |
| color | string | "#848484" | Line color | "#999999" |
| width | number | 1 | Line thickness | 2 |
| dashes | boolean | false | Use dashed line | true |
| smooth | object | continuous | Curve type | {"type": "curvedCW"} |
Edge Smooth Types
The smooth property controls edge curvature:
// Straight line (default)
"smooth": { "type": "continuous", "roundness": 0 }
// Curved clockwise
"smooth": { "type": "curvedCW", "roundness": 0.5 }
// Curved counter-clockwise
"smooth": { "type": "curvedCCW", "roundness": 0.5 }Example Edges
[
{
"from": 1,
"to": 2,
"label": "builds on",
"color": "#999999",
"width": 2,
"smooth": { "type": "continuous" }
},
{
"from": 2,
"to": 3,
"label": "leads to",
"color": "#666666",
"width": 1.5,
"smooth": { "type": "curvedCW" }
},
{
"from": 3,
"to": 4,
"label": "informs",
"color": "#AAAAAA",
"width": 1,
"dashes": true
}
]Color Guidelines
Recommended Color Palettes
Foundation Concepts (Warm Colors):
- Red: #FF6B6B, border: #C92A2A
- Orange: #FF922B, border: #E67700
- Pink: #F06595, border: #C2255C
Intermediate Concepts (Cool Colors):
- Teal: #4ECDC4, border: #0B7285
- Blue: #74C0FC, border: #1971C2
- Cyan: #66D9E8, border: #0C8599
Advanced Concepts (Green/Purple):
- Green: #95E1D3, border: #087F5B
- Lime: #A9E34B, border: #5C940D
- Purple: #B197FC, border: #6741D9
Color Contrast
Ensure sufficient contrast between:
- Node fill color and border color (3:1 ratio minimum)
- Node color and label text (4.5:1 ratio minimum)
- Edge color and background (3:1 ratio minimum)
Layout Guidelines
Positioning Nodes
Coordinate System:
- Origin (0,0) is top-left corner
- X increases to the right
- Y increases downward
- Recommended canvas: 800x600 drawing area
Layout Patterns:
Hierarchical (Top-to-Bottom):
Level 1: y = 100
Level 2: y = 250
Level 3: y = 400
Level 4: y = 550Radial (Circular):
// Center at (400, 300), radius 200
x = centerX + radius * cos(angle)
y = centerY + radius * sin(angle)Grid:
Row 1: y = 100, 200, 300, 400, 500
Row 2: y = 250, ...
Row 3: y = 400, ...Spacing Recommendations
- Minimum node spacing: 100 pixels (center to center)
- Comfortable spacing: 150-200 pixels
- Edge margins: 50 pixels from canvas edges
- Label clearance: 30 pixels below nodes
Complete Example
{
"metadata": {
"title": "Learning Path: Web Development",
"description": "Progressive skill development for web developers",
"author": "Education Team",
"created": "2025-01-09",
"subject": "Computer Science"
},
"groups": {
"basics": {
"color": "#FF6B6B",
"borderColor": "#C92A2A",
"shape": "ellipse"
},
"frontend": {
"color": "#4ECDC4",
"borderColor": "#0B7285",
"shape": "box"
},
"backend": {
"color": "#95E1D3",
"borderColor": "#087F5B",
"shape": "diamond"
}
},
"nodes": [
{
"id": 1,
"label": "HTML Basics",
"group": "basics",
"x": 200,
"y": 100,
"shortDescription": "Fundamental structure of web pages",
"fullDescription": "HTML (HyperText Markup Language) provides the structural foundation for all web pages. Learn to create semantic markup using elements like headings, paragraphs, lists, and links."
},
{
"id": 2,
"label": "CSS Styling",
"group": "basics",
"x": 400,
"y": 100,
"shortDescription": "Visual design and layout control",
"fullDescription": "CSS (Cascading Style Sheets) controls the visual presentation of HTML. Master selectors, properties, layouts (flexbox, grid), and responsive design principles."
},
{
"id": 3,
"label": "JavaScript",
"group": "frontend",
"x": 600,
"y": 100,
"shortDescription": "Interactive behavior and logic",
"fullDescription": "JavaScript adds interactivity to web pages. Learn variables, functions, DOM manipulation, events, and modern ES6+ features for building dynamic user interfaces."
},
{
"id": 4,
"label": "React Framework",
"group": "frontend",
"x": 400,
"y": 300,
"shortDescription": "Component-based UI development",
"fullDescription": "React is a popular library for building user interfaces using reusable components. Learn hooks, state management, props, and component lifecycle to create sophisticated web applications."
},
{
"id": 5,
"label": "Node.js Backend",
"group": "backend",
"x": 600,
"y": 300,
"shortDescription": "Server-side JavaScript runtime",
"fullDescription": "Node.js enables JavaScript on the server. Build RESTful APIs, handle databases, manage authentication, and create full-stack applications using a unified language."
}
],
"edges": [
{
"from": 1,
"to": 2,
"label": "style with",
"color": "#999999",
"width": 2
},
{
"from": 2,
"to": 3,
"label": "enhance with",
"color": "#999999",
"width": 2
},
{
"from": 1,
"to": 4,
"label": "required for",
"color": "#666666",
"width": 1.5
},
{
"from": 2,
"to": 4,
"label": "required for",
"color": "#666666",
"width": 1.5
},
{
"from": 3,
"to": 4,
"label": "builds on",
"color": "#666666",
"width": 1.5
},
{
"from": 3,
"to": 5,
"label": "extends to",
"color": "#666666",
"width": 1.5
}
]
}Validation Checklist
Before deploying an infographic, verify:
- ✅ All node IDs are unique positive integers
- ✅ All edge
fromandtoIDs reference existing nodes - ✅ Node coordinates place all nodes within canvas bounds (0-800, 0-600)
- ✅ Labels are under 30 characters
- ✅ shortDescription exists for every node (for tooltips)
- ✅ fullDescription exists for every node (for detail panel)
- ✅ Colors use valid hex (#RRGGBB) or named colors
- ✅ Groups referenced by nodes are defined in groups section
- ✅ JSON syntax is valid (use JSONLint or similar)
Tips and Best Practices
Content Design
1. Keep labels concise: 15-25 characters ideal 2. Write clear tooltips: One complete sentence 3. Provide context in descriptions: Explain why it matters, not just what it is 4. Use consistent terminology: Align with course vocabulary
Visual Design
1. Limit groups to 5-7: More becomes visually confusing 2. Use color meaningfully: Group by level, category, or theme 3. Maintain visual hierarchy: Size can indicate importance 4. Avoid edge crossings: Plan layout to minimize overlaps
Technical
1. Test with small datasets first: Start with 5-10 nodes 2. Validate JSON: Use online validators before loading 3. Check browser console: Look for parsing errors 4. Scale gradually: Add complexity incrementally
Related Resources
- vis-network documentation: https://visjs.github.io/vis-network/docs/network/
- Color accessibility checker: https://webaim.org/resources/contrastchecker/
- JSON validator: https://jsonlint.com/
- p5.js reference: https://p5js.org/reference/