diff --git a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
index 5182f2959dc571e88b9231c74b68344b4a548321..d1e8b6ba30475c06e9d6c54312e192f26da3a5d3 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.crypto.DecryptionException
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator
+import org.briarproject.bramble.api.identity.IdentityManager
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.messaging.MessagingManager
@@ -37,7 +38,8 @@ interface BriarService {
     fun start(
         contactManager: ContactManager,
         conversationManager: ConversationManager,
-        messagingManager: MessagingManager
+        messagingManager: MessagingManager,
+        identityManager: IdentityManager,
     )
 
     fun stop()
@@ -46,6 +48,7 @@ interface BriarService {
 val CVM = compositionLocalOf<ConversationManager> { error("Undefined ConversationManager") }
 val CTM = compositionLocalOf<ContactManager> { error("Undefined ContactManager") }
 val MM = compositionLocalOf<MessagingManager> { error("Undefined MessagingManager") }
+val IM = compositionLocalOf<IdentityManager> { error("Undefined IdentityManager") }
 
 @Immutable
 @Singleton
@@ -70,7 +73,8 @@ constructor(
     override fun start(
         contactManager: ContactManager,
         conversationManager: ConversationManager,
-        messagingManager: MessagingManager
+        messagingManager: MessagingManager,
+        identityManager: IdentityManager,
     ) {
         val (isDark, setDark) = remember { mutableStateOf(true) }
         val title = "Briar Desktop"
@@ -115,7 +119,8 @@ constructor(
                         CompositionLocalProvider(
                             CVM provides conversationManager,
                             CTM provides contactManager,
-                            MM provides messagingManager
+                            MM provides messagingManager,
+                            IM provides identityManager,
                         ) {
                             BriarUIStateManager(contacts, isDark, setDark)
                         }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/UI.kt b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt
index 871be40b97446240de7204fa2080f84e228bdb91..8f47294dbf2ef35476ab03029ee01fcf1b7baf9c 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/UI.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.account.AccountManager
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator
 import org.briarproject.bramble.api.event.EventBus
+import org.briarproject.bramble.api.identity.IdentityManager
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.introduction.IntroductionManager
 import org.briarproject.briar.api.messaging.MessagingManager
@@ -25,6 +26,7 @@ constructor(
     private val messagingManager: MessagingManager,
     private val introductionManager: IntroductionManager,
     private val conversationManager: ConversationManager,
+    private val identityManager: IdentityManager,
     private val privateMessageFactory: PrivateMessageFactory,
     private val eventBus: EventBus,
     private val passwordStrengthEstimator: PasswordStrengthEstimator
@@ -37,7 +39,12 @@ constructor(
         briarService.start(
             contactManager,
             conversationManager,
-            messagingManager
+            messagingManager,
+            identityManager
         )
     }
+
+    internal fun getContactManager(): ContactManager {
+        return contactManager
+    }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt
index d9d4441b92e0dff1cbc90cf4583e260d4b3c712e..aae22ea21c3d93585ac2ad54352d09dd8d5bd605 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt
@@ -2,13 +2,13 @@ package org.briarproject.briar.desktop.paul.views
 
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.border
+import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
@@ -24,12 +24,12 @@ import androidx.compose.material.icons.filled.WifiTethering
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.res.loadImageBitmap
-import androidx.compose.ui.res.useResource
 import androidx.compose.ui.unit.dp
+import org.briarproject.briar.desktop.IM
+import org.briarproject.briar.desktop.paul.theme.briarBlack
+import org.briarproject.briar.desktop.paul.theme.briarBlue
 import org.briarproject.briar.desktop.paul.theme.sidebarSurface
 
 val SIDEBAR_WIDTH = 56.dp
@@ -42,13 +42,8 @@ fun BriarSidebar(uiMode: UiModes, setUiMode: (UiModes) -> Unit) {
                 modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 5.dp, bottom = 4.dp),
                 onClick = {}
             ) {
-                Image(
-                    bitmap = useResource("images/profile_images/p0.png") { loadImageBitmap(it) },
-                    "my_profile_image",
-                    modifier = Modifier.size(44.dp).align(Alignment.CenterHorizontally).clip(
-                        CircleShape
-                    ).border(2.dp, color = Color.White, CircleShape)
-                )
+                val identityManager = IM.current
+                ProfileCircle(size = 45.dp, identityManager.localAuthor.id.bytes)
             }
             BriarSidebarButton(
                 uiMode = uiMode,
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/Identicon.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/Identicon.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f94e7b36b4e58ddb41f193b0faf5ca0f531a44ab
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/Identicon.kt
@@ -0,0 +1,94 @@
+package org.briarproject.briar.desktop.paul.views
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.DrawScope
+
+/**
+ * Copyright 2014 www.delight.im (info@delight.im)
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+internal class Identicon(private val input: ByteArray, width: Float, height: Float) {
+
+    companion object {
+        private const val ROWS = 9
+        private const val COLUMNS = 9
+        private const val CENTER_COLUMN_INDEX = COLUMNS / 2 + COLUMNS % 2
+    }
+
+    private val colors: Array<Array<Color>>
+    private var cellWidth = 0f
+    private var cellHeight = 0f
+    private fun getByte(index: Int): Byte {
+        return input[index % input.size]
+    }
+
+    init {
+        require(input.isNotEmpty())
+
+        cellWidth = width / COLUMNS
+        cellHeight = height / ROWS
+
+        colors = Array(ROWS) { Array(COLUMNS) { Color(0) } }
+        for (r in 0 until ROWS) {
+            for (c in 0 until COLUMNS) {
+                if (isCellVisible(r, c)) {
+                    colors[r][c] = foregroundColor
+                } else {
+                    colors[r][c] = backgroundColor
+                }
+            }
+        }
+    }
+
+    private fun isCellVisible(row: Int, column: Int): Boolean {
+        val index = 3 + row * CENTER_COLUMN_INDEX + getSymmetricColumnIndex(column)
+        return getByte(index) >= 0
+    }
+
+    private fun getSymmetricColumnIndex(index: Int): Int {
+        return if (index < CENTER_COLUMN_INDEX) index else COLUMNS - index - 1
+    }
+
+    private val foregroundColor: Color
+        get() {
+            val r = getByte(0) * 3 / 4 + 96
+            val g = getByte(1) * 3 / 4 + 96
+            val b = getByte(2) * 3 / 4 + 96
+            return Color(r, g, b)
+        }
+
+    // http://www.google.com/design/spec/style/color.html#color-themes
+    private val backgroundColor: Color
+        get() = Color(0xFA, 0xFA, 0xFA)
+
+    fun draw(g: DrawScope) {
+        for (r in 0 until ROWS) {
+            for (c in 0 until COLUMNS) {
+                val x = cellWidth * c
+                val y = cellHeight * r
+
+                g.drawRect(
+                    color = colors[r][c],
+                    topLeft = Offset(x, y),
+                    size = Size(cellWidth, cellHeight)
+                )
+            }
+        }
+    }
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt
index 11108b19463b963f836195b244991fccc7884baf..29ad9a10a08c7846fca3196ad8394edb9d3bed5f 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt
@@ -3,7 +3,6 @@ package org.briarproject.briar.desktop.paul.views
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.desktop.ui.tooling.preview.Preview
 import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
@@ -67,15 +66,10 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.drawscope.withTransform
-import androidx.compose.ui.res.loadImageBitmap
-import androidx.compose.ui.res.useResource
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import org.briarproject.bramble.api.FormatException
@@ -294,15 +288,6 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont
     )
 }
 
-@Composable
-fun ProfileCircle(bitmap: ImageBitmap, size: Dp) {
-    Image(
-        bitmap,
-        "image",
-        Modifier.size(size).clip(CircleShape).border(2.dp, MaterialTheme.colors.outline, CircleShape)
-    )
-}
-
 @Composable
 fun ContactCard(
     contact: Contact,
@@ -326,7 +311,7 @@ fun ContactCard(
         Row(horizontalArrangement = Arrangement.SpaceBetween) {
             Row(modifier = Modifier.align(Alignment.CenterVertically).padding(horizontal = 16.dp)) {
                 // TODO Pull profile pictures
-                ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 36.dp)
+                ProfileCircle(36.dp, contact.author.id.bytes)
                 // Draw notification badges
                 if (drawNotifications) {
                     Canvas(
@@ -340,7 +325,6 @@ fun ContactCard(
                         }
                     )
                 }
-
                 Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) {
                     Text(
                         contact.author.name,
@@ -578,7 +562,7 @@ fun ConversationHeader(
 
     Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) {
         Row(modifier = Modifier.align(Alignment.Center)) {
-            ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 36.dp)
+            ProfileCircle(36.dp, contact.author.id.bytes)
             Canvas(
                 modifier = Modifier.align(Alignment.CenterVertically),
                 onDraw = {
@@ -704,14 +688,14 @@ fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDra
             }
             Row(Modifier.fillMaxWidth().padding(12.dp), horizontalArrangement = Arrangement.SpaceAround) {
                 Column(Modifier.align(Alignment.CenterVertically)) {
-                    ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 40.dp)
-                    Text(contact.author.name, Modifier.padding(top = 4.dp), fontSize = 16.sp)
+                    ProfileCircle(40.dp, contact.author.id.bytes)
+                    Text(contact.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp)
                 }
                 Icon(Filled.SwapHoriz, "swap", modifier = Modifier.size(48.dp))
                 Column(Modifier.align(Alignment.CenterVertically)) {
                     // TODO Profile pic again
-                    ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 40.dp)
-                    Text(introContact.author.name, Modifier.padding(top = 4.dp), fontSize = 16.sp)
+                    ProfileCircle(40.dp, contact.author.id.bytes)
+                    Text(introContact.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp)
                 }
             }
             var introText by remember { mutableStateOf(TextFieldValue("")) }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/ProfileCircle.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/ProfileCircle.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9fe7dfd346dbd80a8e7f02a588e82b76931075cc
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/ProfileCircle.kt
@@ -0,0 +1,19 @@
+package org.briarproject.briar.desktop.paul.views
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun ProfileCircle(size: Dp, input: ByteArray) {
+    Canvas(Modifier.size(size).clip(CircleShape).border(2.dp, Color.White, CircleShape)) {
+        Identicon(input, this.size.width, this.size.height).draw(this)
+    }
+}