diff --git a/ping/admin.py b/ping/admin.py
index e43d25f202f05c77baf33d788c47ade50750775b..8a9827390482d2b4ce6e8bde7dd5195cf7917802 100644
--- a/ping/admin.py
+++ b/ping/admin.py
@@ -7,4 +7,5 @@ admin.site.register(Agent)
 admin.site.register(Experiment)
 admin.site.register(Metrics)
 admin.site.register(CurrentExperiment)
-admin.site.register(Network)
\ No newline at end of file
+admin.site.register(Network)
+admin.site.register(Link)
\ No newline at end of file
diff --git a/ping/consumers.py b/ping/consumers.py
index 4b5a81a62c4a50ef9fe8cb427eba4f6c0f39f008..0e45079160e57d9b35806f7707f6c84152e3aa57 100644
--- a/ping/consumers.py
+++ b/ping/consumers.py
@@ -57,6 +57,42 @@ class CanvasConsumer(AsyncWebsocketConsumer):
         )
         print("DISCONNECED CODE: ",code)
 
+    async def receive(self, text_data=None, bytes_data=None):
+        data = json.loads(text_data)
+        message = data['message']
+        await self.channel_layer.group_send(
+            self.room_group_name,{
+                "type": 'send_message_to_frontend',
+                "message": message
+            }
+        )
+    async def send_message_to_frontend(self,event):
+        # Receive message from room group
+        message = event['message']
+        # Send message to WebSocket
+        await self.send(text_data=json.dumps({
+            'message': message
+        }))
+
+
+class ComConsumer(AsyncWebsocketConsumer):
+    async def connect(self):
+        self.room_name = 'event'
+        self.room_group_name = self.room_name+"_com"
+        await self.channel_layer.group_add(
+            self.room_group_name,
+            self.channel_name
+        )
+        print(self.room_group_name)
+        await self.accept()
+
+    async def disconnect(self, code):
+        await self.channel_layer.group_discard(
+            self.room_group_name,
+            self.channel_name
+        )
+        print("DISCONNECED CODE: ",code)
+
     async def receive(self, text_data=None, bytes_data=None):
         data = json.loads(text_data)
         message = data['message']
diff --git a/ping/models.py b/ping/models.py
index 58bc5f985890c04f22c16cb45ee8755c145d33f6..abec77261bd3d873244e04c60d66d710140c4754 100644
--- a/ping/models.py
+++ b/ping/models.py
@@ -61,6 +61,8 @@ class CurrentExperiment(models.Model):
     seed = models.CharField(default="0", max_length=64)
     scheduling = models.CharField(default="sync", max_length=10)
 
+    com_graph = models.BooleanField(default=False)
+
 
 class Network(models.Model):
     broker_ip = models.CharField(max_length=60)
@@ -70,3 +72,8 @@ class Network(models.Model):
     iotamak_core_version = models.CharField(max_length=60)
 
     path_to_iotamak = models.CharField(max_length=60)
+
+class Link(models.Model):
+    id1 = models.PositiveIntegerField(default=0)
+    id2 = models.PositiveIntegerField(default=0)
+    value = models.PositiveIntegerField(default=1)
\ No newline at end of file
diff --git a/ping/routing.py b/ping/routing.py
index 758b3413366d99133dd7c9484cc6f9f5500de9fe..cc4f81c7d2864fe46cfef442b10572862aca5d64 100644
--- a/ping/routing.py
+++ b/ping/routing.py
@@ -6,4 +6,5 @@ from . import consumers
 websocket_urlpatterns = [
     re_path("ws/canvas/", consumers.CanvasConsumer.as_asgi()),
     re_path("ws/graph/", consumers.GraphConsumer.as_asgi()),
+    re_path("ws/com/", consumers.ComConsumer.as_asgi()),
 ]
\ No newline at end of file
diff --git a/ping/templates/ping/play.html b/ping/templates/ping/play.html
index 48d9d4b708c1f726d176d0f0d6d3916b5c09af93..294e493af869af4e1ebdfcd1c18d9d735ede7e79 100644
--- a/ping/templates/ping/play.html
+++ b/ping/templates/ping/play.html
@@ -4,6 +4,8 @@
 <head>
   <meta charset="utf-8">
   {% load static %}
+<!-- Load d3.js -->
+<script src="https://d3js.org/d3.v7.min.js"></script>
   <link rel="stylesheet" type="text/css" href="{% static 'ping/index.css' %}">
   <script
           src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"
@@ -14,6 +16,25 @@
 
   <title>Play - IOTAMAK</title>
   <meta name="viewport" content="width=device-width, initial-scale=1">
+
+<style>
+
+.links line {
+  stroke: #999;
+  stroke-opacity: 0.6;
+}
+
+.nodes circle {
+  stroke: #fff;
+  stroke-width: 5px;
+}
+
+text {
+  font-family: sans-serif;
+  font-size: 15px;
+}
+
+</style>
 </head>
 
 <body>
@@ -105,6 +126,7 @@
   {% endif %}
 
 
+  {% if canvas %}
 <canvas id="line-chart" width="800" height="450"></canvas>
 <script>
 
@@ -144,6 +166,87 @@ const myChart = new Chart(document.getElementById("line-chart"), {
 
     };
 </script>
+  {% endif %}
+
+
+<svg width="960" height="600"></svg>
+<script>
+
+var graph = {
+  "nodes": [
+            {"id": "Agent_0", "group": 1},
+            {"id": "Agent_1", "group": 2},
+            {"id": "Agent_2", "group": 1},
+            {"id": "Agent_3", "group": 3}
+        ],
+  "links": []
+};
+
+
+
+
+var svg = d3.select("svg"),
+    width = +svg.attr("width"),
+    height = +svg.attr("height");
+
+var color = d3.scaleOrdinal(d3.schemeCategory20);
+
+var simulation = d3.forceSimulation()
+    .force("link", d3.forceLink().id(function(d) { return d.id; }))
+    .force("charge", d3.forceManyBody().strength(-2500))
+    .force("center", d3.forceCenter(width / 2, height / 2));
+
+  var link = svg.append("g").attr("class", "links").selectAll("line")
+  .data(graph.links).enter().append("line")
+  .attr("stroke-width", function(d) { return Math.sqrt(d.value); });
+
+  var node = svg.append("g")
+      .attr("class", "nodes")
+    .selectAll("g")
+    .data(graph.nodes)
+    .enter().append("g")
+
+  var circles = node.append("circle")
+    .attr("r", 20)
+    .attr("fill", function(d) { return color(d.group); });
+
+  var lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);
+
+  node.append("title").text(function(d) { return d.id; });
+
+  simulation.nodes(graph.nodes).on("tick", ticked);
+  simulation.force("link").links(graph.links);
+
+  function ticked() {
+    link
+        .attr("x1", function(d) { return d.source.x; })
+        .attr("y1", function(d) { return d.source.y; })
+        .attr("x2", function(d) { return d.target.x; })
+        .attr("y2", function(d) { return d.target.y; });
+
+    node
+        .attr("transform", function(d) {
+          return "translate(" + d.x + "," + d.y + ")";
+        })
+  };
+
+const comSocket = new WebSocket(
+  'ws://'
+  + window.location.host
+  + '/ws/com/'
+);
+
+comSocket.onmessage = function (e) {
+  graph = JSON.parse(e.data).message;
+  console.log(graph);
+
+  //simulation.nodes(graph.nodes);
+  simulation.force("link").links(graph.links);
+  simulation.alpha(1).restart();
+  };
+</script>
+
+
 </body>
 
 </html>
\ No newline at end of file
diff --git a/ping/views/play.py b/ping/views/play.py
index 046b2aa38bcc763b3f07f26e74f02f3a2bee9aa9..4018f9bdaa5e6c65dcd5f5490860e3803b4628b6 100644
--- a/ping/views/play.py
+++ b/ping/views/play.py
@@ -16,8 +16,8 @@ from django.urls import reverse
 from iotAmak.tool.ssh_client import Cmd
 from paho.mqtt.client import Client as MQTTClient
 
-from .tool import delete_folder, get_ssh_client, canvas_event_triger, graph_event_triger
-from ..models import Experiment, CurrentExperiment, Metrics, Network
+from .tool import delete_folder, get_ssh_client, canvas_event_triger, graph_event_triger, com_event_triger
+from ..models import Experiment, CurrentExperiment, Metrics, Network, Link
 
 client = MQTTClient(client_id="django-ihm")
 
@@ -34,6 +34,18 @@ def agent_log(client, userdata, message) -> None:
     print("[Log] " + str(message.payload.decode("utf-8")) + " on topic " + message.topic)
 
 
+def agent_message(client, userdata, message) -> None:
+    id1 = literal_eval(message.payload.decode("utf-8")).get("id")
+    id2 = int(message.topic.split("/")[1])
+
+    if id1 > id2:
+        id1, id2 = id2, id1
+
+    link = Link.objects.filter(id1=id1, id2=id2)[0]
+    link.value += 1
+    link.save()
+
+
 def agent_metric(client, userdata, message) -> None:
     results = literal_eval(message.payload.decode("utf-8"))
 
@@ -56,15 +68,39 @@ def agent_metric(client, userdata, message) -> None:
             data += 1
     graph_event_triger([data, exp.cycle])
 
+
 def cycle_done(client, userdata, message) -> None:
     cur_exp = CurrentExperiment.objects.all()[0]
     cur_exp.cycle += 1
     cur_exp.save()
 
+    data = {
+        "nodes": [
+            {"id": "Agent_0", "group": 1},
+            {"id": "Agent_1", "group": 2},
+            {"id": "Agent_2", "group": 1},
+            {"id": "Agent_3", "group": 3}
+        ],
+        "links": [
+            {
+                "source": "Agent_"+str(elem.id1),
+                "target": "Agent_"+str(elem.id2),
+                "value": elem.value
+            }
+            for elem in Link.objects.all()
+        ]
+    }
+    com_event_triger(str(data))
+
 
 def update_nbr_agent(client, userdata, message) -> None:
     exp = CurrentExperiment.objects.all()[0]
-    subscribe(client, "agent/"+str(exp.nbr_agent)+"/log", agent_log)
+    subscribe(client, "agent/" + str(exp.nbr_agent) + "/log", agent_log)
+    if exp.com_graph:
+        subscribe(client, "agent/" + str(exp.nbr_agent) + "/mail", agent_message)
+        for i in range(exp.nbr_agent):
+            link = Link(id1=i, id2=exp.nbr_agent)
+            link.save()
     exp.nbr_agent += 1
     exp.save()
 
@@ -84,14 +120,18 @@ def experiment_load(request):
     CurrentExperiment.objects.all().delete()
     cur_exp = CurrentExperiment(name=Experiment.objects.get(status="Selected").name)
 
-    with open(str(settings.MEDIA_ROOT) + "current_experiment/"+str(exp.name)+"/config.json") as json_file:
+    with open(str(settings.MEDIA_ROOT) + "current_experiment/" + str(exp.name) + "/config.json") as json_file:
         config = json.load(json_file)
 
     if "seed" in config:
         cur_exp.seed = str(config.get("seed"))
     else:
         random.seed()
-        cur_exp.seed = str(random.randint(0,9999999))
+        cur_exp.seed = str(random.randint(0, 9999999))
+
+    if "com_graph" in config:
+        cur_exp.com_graph = True
+        Link.objects.all().delete()
 
     cur_exp.scheduling = config.get("scheduling_type")
 
@@ -99,6 +139,7 @@ def experiment_load(request):
 
     return HttpResponseRedirect(reverse('ping:play'))
 
+
 def delete_nohup():
     ssh = get_ssh_client()
 
@@ -112,6 +153,7 @@ def delete_nohup():
         print(" Ip : ", ssh.clients[i_client].hostname)
         ssh.run_cmd(i_client, commands)
 
+
 def get_nohup():
     ssh = get_ssh_client()
 
@@ -125,6 +167,7 @@ def get_nohup():
         print(" Ip : ", ssh.clients[i_client].hostname)
         ssh.run_cmd(i_client, commands)
 
+
 def experiment_start(request):
     n = Network.objects.all()[0]
     ssh = get_ssh_client()
@@ -157,9 +200,9 @@ def experiment_start(request):
         "broker_ip": n.broker_ip,
         "clients": str([c.to_send() for c in ssh.clients]),
         "seed": exp.seed,
-        "iot_path": n.path_to_iotamak+"example/",
+        "iot_path": n.path_to_iotamak + "example/",
         "broker_username": n.broker_username,
-        "broker_password":  n.broker_password
+        "broker_password": n.broker_password
     }
     if not exp.scheduling == "sync":
         amas_dict["wait_delay"] = str(5)
@@ -169,7 +212,7 @@ def experiment_start(request):
         "broker_ip": n.broker_ip,
         "seed": exp.seed,
         "broker_username": n.broker_username,
-        "broker_password":  n.broker_password
+        "broker_password": n.broker_password
     }
     if not exp.scheduling == "sync":
         env_dict["wait_delay"] = str(5)
@@ -285,12 +328,13 @@ def experiment_metric(request, metrics_id):
             for metric in raw_metrics[0].metrics if metric != {}]
         return JsonResponse({'data': cycle_metrics})
 
+
 def export_csv(request):
     response = HttpResponse(content_type='text/csv')
     response['Content-Disposition'] = 'attachment; filename="users.csv"'
 
     writer = csv.writer(response)
-    writer.writerow(['cycle'] + ["Agent_"+key for key in Metrics.objects.order_by('?').first().metrics[0].keys()])
+    writer.writerow(['cycle'] + ["Agent_" + key for key in Metrics.objects.order_by('?').first().metrics[0].keys()])
 
     for m in Metrics.objects.all():
         cycle = m.cycle
@@ -299,4 +343,4 @@ def export_csv(request):
             if metric != {}:
                 writer.writerow([cycle] + [value for value in metric.values()])
 
-    return response
\ No newline at end of file
+    return response
diff --git a/ping/views/tool.py b/ping/views/tool.py
index 5eb763ee76780a098187122a3199e1a88f698685..35036dc00cdf49ab55e1d92b283295f0b46ecbac 100644
--- a/ping/views/tool.py
+++ b/ping/views/tool.py
@@ -55,3 +55,13 @@ def graph_event_triger(metrics):
             'message': metrics
         }
     )
+
+def com_event_triger(data):
+    channel_layer = get_channel_layer()
+    async_to_sync(channel_layer.group_send)(
+        'event_com',
+        {
+            'type': 'send_message_to_frontend',
+            'message': data
+        }
+    )
\ No newline at end of file