Custom events
Custom events represent discrete moments in your application, things that happen at a specific point in time. Think of them as the XR equivalent of traditional analytics events like "button clicked" or "page viewed", but extended to cover spatial interactions.
Each event consists of:
- A name describing what happened (e.g., "Model loaded", "Annotation placed")
- Optional properties providing context (e.g., model name, file size, user action)
Events are timestamped automatically and associated with the user's current position, giving you spatial context for every action.
Use custom events to track:
- User-initiated actions — Loading a file, placing an annotation, sharing with a collaborator
- System events — Model finished loading, network reconnected, error occurred
- Milestones — Tutorial completed, first model imported, session ended
- Feature usage — Which tools users select, which menu options they access
Custom events are ideal for answering questions like: "What features do users engage with most?" or "Where in the workflow do users encounter errors?"
Basic Usage
For simple events without additional context:
// Track that something happened
Cognitive3DManager.sendCustomEvent("User opened file browser")
// Track that something happened
Cognitive3DManager.sendCustomEvent("User opened file browser");
Events with Properties
Most events benefit from contextual properties:
val properties = mapOf(
"model_name" to "nice_home_3d",
"file_type" to "glb",
"load_status" to "success",
"file_size_mb" to 24.5,
"load_duration_ms" to 3200
)
Cognitive3DManager.sendCustomEvent("3D model loaded", properties)
Map<String, Object> properties = new HashMap<>();
properties.put("model_name", "nice_home_3d");
properties.put("file_type", "glb");
properties.put("load_status", "success");
properties.put("file_size_mb", 24.5);
properties.put("load_duration_ms", 3200);
Cognitive3DManager.sendCustomEvent("3D model loaded", properties);
Note
Properties can include strings, numbers, and booleans. Keep property values simple. Avoid nested objects or arrays.
Examples
Custom events are ideal for tracking discrete user actions and system occurrences. Include relevant properties to provide context for analysis.
Tracking model sharing between collaborators
Understanding how users share content helps identify collaboration patterns and popular sharing methods. Capture the sharing context to analyze which workflows are most common:
fun onModelShared(model: Model, recipients: List<Collaborator>, method: ShareMethod) {
Cognitive3DManager.sendCustomEvent("Model shared", mapOf(
"model_id" to model.id,
"model_name" to model.displayName,
"recipient_count" to recipients.size,
"share_method" to method.name, // "direct_invite", "link", "workspace_broadcast"
"model_size_mb" to model.fileSizeMb
))
}
public void onModelShared(Model model, List<Collaborator> recipients, ShareMethod method) {
Map<String, Object> properties = new HashMap<>();
properties.put("model_id", model.getId());
properties.put("model_name", model.getDisplayName());
properties.put("recipient_count", recipients.size());
properties.put("share_method", method.name());
properties.put("model_size_mb", model.getFileSizeMb());
Cognitive3DManager.sendCustomEvent("Model shared", properties);
}
Tracking spatial annotations
Annotations reveal how users communicate and mark up 3D content. Track annotation placement to understand which annotation types are most useful and where users focus their feedback:
fun onAnnotationPlaced(annotation: Annotation, targetObject: DynamicObject?) {
Cognitive3DManager.sendCustomEvent("Annotation placed", mapOf(
"annotation_type" to annotation.type.name,
"annotation_id" to annotation.id,
"target_object_id" to (targetObject?.id ?: "world"),
"duration_seconds" to annotation.durationSeconds,
"has_attachment" to annotation.hasAttachment
))
}
public void onAnnotationPlaced(Annotation annotation, @Nullable DynamicObject targetObject) {
Map<String, Object> properties = new HashMap<>();
properties.put("annotation_type", annotation.getType().name());
properties.put("annotation_id", annotation.getId());
properties.put("target_object_id", targetObject != null ? targetObject.getId() : "world");
properties.put("duration_seconds", annotation.getDurationSeconds());
properties.put("has_attachment", annotation.hasAttachment());
Cognitive3DManager.sendCustomEvent("Annotation placed", properties);
}
Tracking errors for debugging and UX improvement
Tracking failures is just as important as tracking successes. Error events help identify pain points, prioritize fixes, and understand where users struggle:
fun onModelLoadFailed(fileName: String, error: LoadError) {
Cognitive3DManager.sendCustomEvent("Model load failed", mapOf(
"model_name" to fileName,
"error_code" to error.code,
"error_message" to error.userMessage,
"error_category" to error.category.name, // "network", "format", "memory", "permission"
"retry_count" to error.retryAttempts
))
}
public void onModelLoadFailed(String fileName, LoadError error) {
Map<String, Object> properties = new HashMap<>();
properties.put("model_name", fileName);
properties.put("error_code", error.getCode());
properties.put("error_message", error.getUserMessage());
properties.put("error_category", error.getCategory().name());
properties.put("retry_count", error.getRetryAttempts());
Cognitive3DManager.sendCustomEvent("Model load failed", properties);
}
Best Practices
Property guidelines
- Use consistent key naming with ASCII characters. Values should be strings, numbers, or booleans.
- Include units in key names when relevant (
duration_seconds,size_mb,latency_ms) - Limit properties to 10 key-value pairs maximum
- Only include properties that will be useful for analysis
What not to track
- Avoid high-frequency events (use Sensors instead for continuous data)
- Don't include sensitive user data in properties
- Skip redundant events that duplicate information available elsewhere
If you have a question or any feedback about our documentation please use the Intercom button (purple circle) in the lower right corner of any web page or join our Discord.