Social Analyzer provides powerful visualization capabilities through force-directed graphs that help investigators understand relationships between profiles, metadata, and patterns.
Overview
The visualization module (visualize.js) uses Ixora, a force-directed graph library, to create interactive visualizations of:
- Detected profiles and their relationships
- Metadata extracted from profiles
- Cross-platform connections
- Multi-profile correlations
Force-Directed Graphs
Force-directed graphs display nodes (profiles, metadata) and edges (relationships) in a physics-based layout where connected items attract and unconnected items repel each other.
Benefits
- Visual Pattern Recognition: Quickly identify clusters and connections
- Interactive Exploration: Search, filter, and navigate through data
- Relationship Mapping: See how profiles relate across platforms
- Metadata Visualization: Display extracted information hierarchically
How It Works
From visualize.js:4-85, the visualization engine:
async function visualize_force_graph (req, detected, type) {
const graph = new QBIxora('Social-Analyzer', false)
const temp_filtered = detected.filter(item => item.status === 'good')
if (temp_filtered.length > 0) {
// Add root node for the username
graph.add_node(req.body.string, req.body.string, {
header: req.body.string
})
// Process each detected profile
temp_filtered.forEach(site => {
// Add profile node
graph.add_node(site.link, site.link, {
header: site.link
})
// Connect profile to username
graph.add_edge(site.username, site.link, {
width: 1
})
// Add metadata nodes if available
if ('metadata' in site) {
if (site.metadata !== 'unavailable' && site.metadata.length > 0) {
site.metadata.forEach(meta => {
if ('content' in meta) {
// Create metadata label
let temp_string = ''
if ('name' in meta) {
temp_string = meta.name + ' -> ' + meta.content
} else if ('itemprop' in meta) {
temp_string = meta.itemprop + ' -> ' + meta.content
} else if ('property' in meta) {
temp_string = meta.property + ' -> ' + meta.content
}
// Truncate long strings
if (temp_string.length > 70) {
temp_string = temp_string.substring(0, 70)
.replace(/\r?\n|\r/g, '') + '..'
}
// Add metadata node
graph.add_node(temp_string, temp_string, {
header: temp_string
})
// Connect metadata to profile
graph.add_edge(site.link, temp_string, {
width: 1
})
}
})
}
}
})
}
const ret_graph = graph.create_graph(
'#ixora-graph',
'Ixora random nodes example',
'Search Box',
'Search in extracted patterns',
'https://github.com/qeeqbox/ixora',
'Qeeqbox-ixora',
['search', 'tooltip'],
10,
100,
graph.graph,
'object',
undefined,
undefined
)
return ret_graph
}
Graph Structure
Node Types
- Root Node: The searched username(s)
- Profile Nodes: Detected social media profiles
- Metadata Nodes: Extracted metadata from profiles
Edge Types
- Username → Profile: Direct connection from search term to detected profile
- Profile → Metadata: Connection from profile to its metadata
Example Structure
johndoe (root)
├─→ twitter.com/johndoe (profile)
│ ├─→ og:title -> John Doe (metadata)
│ ├─→ og:description -> Developer (metadata)
│ └─→ og:image -> https://... (metadata)
├─→ github.com/johndoe (profile)
│ ├─→ description -> Open Source Contributor (metadata)
│ └─→ location -> San Francisco (metadata)
└─→ linkedin.com/in/johndoe (profile)
├─→ og:title -> John Doe - LinkedIn (metadata)
└─→ description -> Software Engineer (metadata)
Multi-Profile Visualization
When searching multiple usernames simultaneously, the visualization creates a hierarchical structure.
From visualize.js:9-22:
if (req.body.group) {
// Add root node for all usernames
graph.add_node(req.body.string, req.body.string, {
header: req.body.string
})
// Add individual username nodes
req.body.string.split(',').forEach(username => {
graph.add_node(username, username, {
header: username
})
// Connect username to root
graph.add_edge(username, req.body.string, {
width: 1
})
})
}
Multi-Profile Structure
"johndoe,janedoe" (root)
├─→ johndoe (username)
│ ├─→ twitter.com/johndoe
│ └─→ github.com/johndoe
└─→ janedoe (username)
├─→ twitter.com/janedoe
└─→ github.com/janedoe
Usage
Basic Visualization
# Generate visualization (requires metadata)
node app.js --username "johndoe" --metadata
# View the graph
# Open http://localhost:9005/graph.html in browser
Multi-Profile Visualization
# Visualize multiple related profiles
node app.js --username "johndoe,jdoe,john_doe" --metadata
# Include extracted patterns in visualization
node app.js --username "johndoe" --metadata --extract
Visualization requires the --metadata flag. Without metadata, the graph will only show username and profile nodes without detailed information.
Interactive Features
The Ixora-powered visualization provides several interactive features:
Search Functionality
From visualize.js:73, the graph includes a search box:
const ret_graph = graph.create_graph(
'#ixora-graph',
'Ixora random nodes example',
'Search Box', // Search box label
'Search in extracted patterns', // Search placeholder
'https://github.com/qeeqbox/ixora',
'Qeeqbox-ixora',
['search', 'tooltip'], // Enabled features
// ...
)
Hover over nodes to see detailed information about:
- Profile URLs
- Metadata content
- Relationships
Physics Simulation
The graph uses physics parameters:
- Charge: 10 (repulsion strength)
- Distance: 100 (preferred edge length)
These create natural clustering of related information.
Long metadata strings are automatically truncated for better visualization.
From visualize.js:51-54:
if (temp_string.length > 70) {
temp_string = temp_string.substring(0, 70).replace(/\r?\n|\r/g, '') + '..'
} else {
temp_string = temp_string.replace(/\r?\n|\r/g, '')
}
This ensures:
- Readable node labels
- Clean graph appearance
- Better performance with many nodes
Accessing the Visualization
Web Interface
The visualization is available through the web app:
-
Start Social Analyzer:
-
Navigate to:
http://localhost:9005/app.html
-
Enter username and enable metadata extraction
-
View results with the graph visualization
Direct Graph Access
The graph is saved to a static file:
# The graph HTML is saved at:
public/graph.html
# Access directly:
http://localhost:9005/graph.html
Graph Initialization
From visualize.js integration in helper.js:81-83:
let temp_ixora = new QBIxora('Social-Analyzer', false)
temp_ixora.save_base_html(public_graph_path)
temp_ixora = null
The base graph template is initialized when the application starts.
Practical Applications
OSINT Investigations
Visualization helps investigators:
- Identify profile clusters
- Find shared metadata across platforms
- Discover hidden connections
- Map digital footprints
Example Investigation Flow
# 1. Search with metadata extraction
node app.js --username "johndoe" --metadata --extract --filter good
# 2. View visualization
# Open http://localhost:9005/graph.html
# 3. Analyze patterns
# - Look for shared email addresses
# - Check for consistent profile descriptions
# - Identify cross-platform links
# 4. Expand search based on findings
node app.js --username "johndoe,newfindingusername" --metadata --extract
Multi-Target Correlation
# Compare multiple suspects
node app.js --username "suspect1,suspect2,suspect3" \
--metadata --extract --filter good
# Visualization shows:
# - Shared profiles
# - Common metadata
# - Potential connections
Graph Data Export
The visualization function returns structured graph data:
return {
graph: {
nodes: [
{ id: 'johndoe', label: 'johndoe' },
{ id: 'twitter.com/johndoe', label: 'twitter.com/johndoe' },
// ...
],
links: [
{ source: 'johndoe', target: 'twitter.com/johndoe' },
// ...
]
}
}
This data can be:
- Saved to JSON files
- Imported into other visualization tools
- Processed for further analysis
Node Limits
For optimal performance:
- < 100 nodes: Smooth interaction
- 100-500 nodes: May experience slight lag
- > 500 nodes: Consider filtering results
Optimization Strategies
- Filter by status: Use
--filter good to show only confirmed profiles
- Limit websites: Use
--top 50 or specific --websites
- Selective metadata: Only extract metadata for high-priority platforms
# Optimized visualization command
node app.js --username "johndoe" \
--metadata --filter good --top 50
Customization
The Ixora graph can be customized by modifying parameters in visualize.js:73:
const ret_graph = graph.create_graph(
'#ixora-graph', // Container ID
'Social Analyzer Graph', // Title
'Search', // Search label
'Search nodes...', // Placeholder
'https://...', // Link URL
'Project Name', // Link text
['search', 'tooltip'], // Features
10, // Charge (repulsion)
100, // Distance (edge length)
graph.graph, // Graph data
'object', // Return type
undefined, // Custom callback
undefined // Custom options
)
Experiment with charge and distance parameters to adjust how tightly nodes cluster together. Lower charge values create tighter clusters.
Troubleshooting
Empty Graph
Problem: Graph shows no nodes
Solutions:
- Ensure
--metadata flag is enabled
- Check that profiles were detected (
--filter good)
- Verify network connectivity
Too Many Nodes
Problem: Graph is cluttered and slow
Solutions:
- Use
--filter good to show only confirmed profiles
- Limit websites with
--top N or --websites
- Disable pattern extraction if not needed
Problem: Only username and profile nodes visible
Solutions:
- Confirm
--metadata flag is set
- Check that detected profiles have metadata
- Try slow mode for better metadata extraction
Large graphs with 500+ nodes may impact browser performance. Consider filtering your results for better visualization experience.