The Friendship Transaction
About This Sketch
A network visualization exploring the uncomfortable reality that all friendships involve value exchange. Nodes represent people, connections show relationships, and colors indicate balance—bright for healthy reciprocity, muted for imbalances. Arrows point toward whoever's receiving more. Watch as relationships drift and occasionally rebalance, making visible the usually invisible accounting we all do in social bonds.
Algorithm
This sketch visualizes friendship as a network of value exchanges between people. Each node represents a person, and the connections between them represent friendships with bidirectional value flows.
The sketch tracks how much value each person gives and receives in each relationship, calculating a "balance" for each connection. Connections are color-coded based on balance: bright color for balanced trades (both parties getting roughly equal value), and muted colors for imbalanced trades (one party giving more than receiving).
Arrows on the connections point toward whoever is receiving more value, making the asymmetry visible. The nodes drift gently to create organic movement, and the relationships occasionally rebalance over time to simulate how friendships evolve.
This visualization makes explicit the usually invisible accounting we all do in relationships—showing that healthy friendships (balanced connections) can coexist with various forms of imbalanced exchanges.
Pseudocode
SETUP:
Create 8 friend nodes positioned in a loose circle
For each node, create 2-3 random connections to other nodes
Assign random "give" and "receive" values to each connection
EACH FRAME:
Clear background
For each node:
Apply gentle random drift
Calculate balance for each connection (received - given)
Draw connections with color based on balance:
- Balanced (|balance| < 0.3): accent1 (healthy)
- Receiving more (balance > 0): accent3 (taking)
- Giving more (balance < 0): accent2 (giving)
Draw arrow pointing toward whoever receives more
Draw the node itself
Every 200 frames:
Randomly adjust one connection's give/receive values
(Simulates relationship rebalancing over time)
Source Code
let sketch = function(p) {
let nodes = [];
let numNodes = 8;
class FriendNode {
constructor(x, y, id) {
this.x = x;
this.y = y;
this.id = id;
this.vx = 0;
this.vy = 0;
this.connections = [];
this.valueGiven = {};
this.valueReceived = {};
this.size = 12;
}
addConnection(otherNode, giveAmount, receiveAmount) {
this.connections.push({
node: otherNode,
give: giveAmount,
receive: receiveAmount
});
this.valueGiven[otherNode.id] = giveAmount;
this.valueReceived[otherNode.id] = receiveAmount;
}
getBalance(otherNode) {
let given = this.valueGiven[otherNode.id] || 0;
let received = this.valueReceived[otherNode.id] || 0;
return received - given; // Positive means receiving more
}
update() {
// Gentle drift
this.vx += p.random(-0.1, 0.1);
this.vy += p.random(-0.1, 0.1);
// Damping
this.vx *= 0.95;
this.vy *= 0.95;
this.x += this.vx;
this.y += this.vy;
// Constrain to canvas
this.x = p.constrain(this.x, 40, 360);
this.y = p.constrain(this.y, 40, 260);
}
display(colors) {
// Draw connections first (so nodes appear on top)
for (let conn of this.connections) {
let balance = this.getBalance(conn.node);
let strokeColor;
let strokeAlpha;
if (p.abs(balance) < 0.3) {
// Balanced trade - healthy green-ish (accent1)
strokeColor = colors.accent1;
strokeAlpha = 180;
} else if (balance > 0) {
// Receiving more - yellowish (accent3)
strokeColor = colors.accent3;
strokeAlpha = 120;
} else {
// Giving more - reddish (accent2)
strokeColor = colors.accent2;
strokeAlpha = 120;
}
p.stroke(...strokeColor, strokeAlpha);
p.strokeWeight(p.map(p.abs(balance), 0, 2, 1, 3));
// Draw arrow to show direction of flow
let midX = (this.x + conn.node.x) / 2;
let midY = (this.y + conn.node.y) / 2;
p.line(this.x, this.y, conn.node.x, conn.node.y);
// Draw small arrow indicating flow direction
let angle = p.atan2(conn.node.y - this.y, conn.node.x - this.x);
let arrowSize = 6;
if (balance !== 0) {
// Arrow points toward whoever is receiving more
if (balance > 0) {
// This node receiving more, arrow points to it
angle += p.PI;
}
p.push();
p.translate(midX, midY);
p.rotate(angle);
p.fill(...strokeColor, strokeAlpha);
p.noStroke();
p.triangle(-arrowSize, -arrowSize/2, -arrowSize, arrowSize/2, 0, 0);
p.pop();
}
}
// Draw node
p.fill(...colors.accent1);
p.noStroke();
p.circle(this.x, this.y, this.size);
// Inner circle
p.fill(...colors.accent2);
p.circle(this.x, this.y, this.size * 0.5);
}
}
p.setup = function() {
p.createCanvas(400, 300);
// Create nodes in a loose circle
for (let i = 0; i < numNodes; i++) {
let angle = (i / numNodes) * p.TWO_PI;
let radius = 80;
let x = 200 + p.cos(angle) * radius + p.random(-20, 20);
let y = 150 + p.sin(angle) * radius + p.random(-20, 20);
nodes.push(new FriendNode(x, y, i));
}
// Create connections with varying balance
for (let i = 0; i < nodes.length; i++) {
// Each node connects to 2-3 others
let numConnections = p.floor(p.random(2, 4));
for (let j = 0; j < numConnections; j++) {
let otherIndex = p.floor(p.random(nodes.length));
if (otherIndex !== i) {
let giveAmount = p.random(0.5, 2);
let receiveAmount = p.random(0.5, 2);
nodes[i].addConnection(nodes[otherIndex], giveAmount, receiveAmount);
}
}
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
// Update and display all nodes
for (let node of nodes) {
node.update();
node.display(colors);
}
// Draw title
p.fill(...colors.accent3);
p.noStroke();
p.textAlign(p.CENTER);
p.textSize(11);
p.text('The Friendship Network: Value Flows', 200, 20);
// Draw legend
p.textAlign(p.LEFT);
p.textSize(8);
p.stroke(...colors.accent1, 180);
p.strokeWeight(2);
p.line(15, 40, 35, 40);
p.noStroke();
p.fill(...colors.accent3);
p.text('Balanced', 40, 42);
p.stroke(...colors.accent3, 120);
p.strokeWeight(2);
p.line(15, 52, 35, 52);
p.noStroke();
p.text('Receiving more', 40, 54);
p.stroke(...colors.accent2, 120);
p.strokeWeight(2);
p.line(15, 64, 35, 64);
p.noStroke();
p.text('Giving more', 40, 66);
// Occasional rebalancing animation
if (p.frameCount % 200 === 0) {
// Randomly adjust some connections
let node = p.random(nodes);
if (node.connections.length > 0) {
let conn = p.random(node.connections);
conn.give += p.random(-0.2, 0.2);
conn.receive += p.random(-0.2, 0.2);
}
}
};
};