diff --git a/playground/core-januscaler-demo-vue/src/pages/videocall.vue b/playground/core-januscaler-demo-vue/src/pages/videocall.vue
index 4e197d0..90ff9a8 100644
--- a/playground/core-januscaler-demo-vue/src/pages/videocall.vue
+++ b/playground/core-januscaler-demo-vue/src/pages/videocall.vue
@@ -1,17 +1,52 @@
-videocall
+
+
+
+
+
+
+
+
+
+
+
+
+
Ongoing call...
+
-
\ No newline at end of file
+
diff --git a/playground/core-januscaler-demo-vue/src/services/videocall/manager.ts b/playground/core-januscaler-demo-vue/src/services/videocall/manager.ts
new file mode 100644
index 0000000..83900fa
--- /dev/null
+++ b/playground/core-januscaler-demo-vue/src/services/videocall/manager.ts
@@ -0,0 +1,67 @@
+import { JanuScaler } from '@januscaler/browser-sdk';
+import { Ref } from 'vue';
+
+export class VideoCallManager {
+ private client: any;
+ private videoCallHandle: any;
+ private localVideoElement: Ref;
+ private remoteVideoElement: Ref;
+ private remoteAudioElement: Ref;
+
+ constructor({
+ token,
+ localVideoElement,
+ remoteVideoElement,
+ remoteAudioElement,
+ }: {
+ token: string;
+ localVideoElement: Ref;
+ remoteVideoElement: Ref;
+ remoteAudioElement: Ref;
+ }) {
+ this.client = new JanuScaler({ token });
+ this.localVideoElement = localVideoElement;
+ this.remoteVideoElement = remoteVideoElement;
+ this.remoteAudioElement = remoteAudioElement;
+ }
+
+ async init() {
+ await this.client.init();
+ this.videoCallHandle = await this.client.videoCall.createServiceHandle();
+ console.log('VideoCallManager initialized');
+ }
+
+ async register(username: string) {
+ if (!this.videoCallHandle) await this.init();
+ await this.videoCallHandle.register(username);
+ console.log(`Registered as ${username}`);
+ }
+
+ async call(username: string) {
+ if (!this.videoCallHandle) throw new Error('Service handle not initialized');
+
+ const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
+
+ // Bind local video stream
+ if (this.localVideoElement.value) {
+ this.localVideoElement.value.srcObject = mediaStream;
+ }
+
+ mediaStream.getTracks().forEach((track) => {
+ this.videoCallHandle.addTrack(track);
+ });
+
+ const offer = await this.videoCallHandle.createOffer();
+ await this.videoCallHandle.setLocalDescription(offer);
+ await this.videoCallHandle.call(username, offer);
+
+ this.videoCallHandle.onTrack = (track:any) => {
+ if (track.kind === 'video' && this.remoteVideoElement.value) {
+ this.remoteVideoElement.value.srcObject = track.streams[0];
+ }
+ if (track.kind === 'audio' && this.remoteAudioElement.value) {
+ this.remoteAudioElement.value.srcObject = track.streams[0];
+ }
+ };
+ }
+}