diff --git a/game/engines/default/engine/LogDisplay.lua b/game/engines/default/engine/LogDisplay.lua
index 75b3ea7b9bf9e25254f49b373e978f8ad559bbaf..8b9159d37199f0c977529cbbb17c2c8a54008c4c 100644
--- a/game/engines/default/engine/LogDisplay.lua
+++ b/game/engines/default/engine/LogDisplay.lua
@@ -19,6 +19,7 @@
 
 require "engine.class"
 require "engine.ui.Base"
+local Slider = require "engine.ui.Slider"
 
 --- Module that handles message history in a mouse wheel scrollable zone
 module(..., package.seeall, class.inherit(engine.ui.Base))
@@ -62,15 +63,7 @@ function _M:resize(x, y, w, h)
 		self.bg_texture, self.bg_texture_w, self.bg_texture_h = self.bg_surface:glTexture()
 	end
 
-	local sb, sb_w, sb_h = self:getImage("ui/scrollbar.png")
-	local ssb, ssb_w, ssb_h = self:getImage("ui/scrollbar-sel.png")
-
-	self.scrollbar = { bar = {}, sel = {} }
-	self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.tex, self.scrollbar.sel.texw, self.scrollbar.sel.texh = ssb_w, ssb_h, ssb:glTexture()
-	local s = core.display.newSurface(sb_w, self.h)
-	s:erase(200,0,0)
-	for i = 0, self.h do s:merge(sb, 0, i) end
-	self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.tex, self.scrollbar.bar.texw, self.scrollbar.bar.texh = ssb_w, self.h, s:glTexture()
+	self.scrollbar = Slider.new{size=self.h - 20, max=1, inverse=true}
 end
 
 --- Appends text to the log
@@ -142,10 +135,9 @@ function _M:toScreen()
 	end
 
 	if true then
-		local pos = self.scroll * (self.h - self.fh) / (self.max - self.max_display + 1)
-
-		self.scrollbar.bar.tex:toScreenFull(self.display_x + self.w - self.scrollbar.bar.w, self.display_y, self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.texw, self.scrollbar.bar.texh)
-		self.scrollbar.sel.tex:toScreenFull(self.display_x + self.w - self.scrollbar.sel.w, self.display_y + self.h - self.scrollbar.sel.h - pos, self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.texw, self.scrollbar.sel.texh)
+		self.scrollbar.pos = self.scroll
+		self.scrollbar.max = self.max - self.max_display + 1
+		self.scrollbar:display(self.display_x + self.w - self.scrollbar.w, self.display_y)
 	end
 end
 
diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua
index eb84cfd973d4a78da0a93fd60b4c217c3a55fd5d..d14996c29920d36cc9cc466755b0683d230359a0 100644
--- a/game/engines/default/engine/Module.lua
+++ b/game/engines/default/engine/Module.lua
@@ -239,6 +239,7 @@ function _M:instanciate(mod, name, new_game, no_reboot)
 
 	profile:addStatFields(unpack(mod.profile_stats_fields or {}))
 	profile:loadModuleProfile(mod.short_name)
+	profile:currentCharacter(mod.name, "game did not tell us")
 
 	-- Init the module code
 	local M, W = mod.load("init")
diff --git a/game/engines/default/engine/PlayerProfile.lua b/game/engines/default/engine/PlayerProfile.lua
index e03e900b4bc609b43accfe34555bf5df787acdf2..8fa97a1444c13030e77caa3a9e3cba70d7401936 100644
--- a/game/engines/default/engine/PlayerProfile.lua
+++ b/game/engines/default/engine/PlayerProfile.lua
@@ -523,6 +523,16 @@ function _M:registerSaveChardump(module, uuid, title, tags, data)
 	print("[ONLINE PROFILE] saved character ", uuid)
 end
 
+function _M:currentCharacter(module, title)
+	if not self.auth then return end
+	core.profile.pushOrder(table.serialize{o="CurrentCharacter",
+		module=module,
+		title=title,
+		valid=self.hash_valid,
+	})
+	print("[ONLINE PROFILE] current character ", title)
+end
+
 function _M:newProfile(Login, Name, Password, Email)
 	print("[ONLINE PROFILE] profile options ", Login, Email, Name)
 
diff --git a/game/engines/default/engine/UserChat.lua b/game/engines/default/engine/UserChat.lua
index 32a241379b747dd47bc33eb9de5b7aea15e83027..0e9d3e3969919a4ce4bfefbcdb46fa50c2ce1a15 100644
--- a/game/engines/default/engine/UserChat.lua
+++ b/game/engines/default/engine/UserChat.lua
@@ -21,6 +21,7 @@ require "engine.class"
 require "engine.ui.Base"
 local KeyBind = require "engine.KeyBind"
 local Mouse = require "engine.Mouse"
+local Slider = require "engine.ui.Slider"
 
 --- Module that handles multiplayer chats
 module(..., package.seeall, class.inherit(engine.ui.Base))
@@ -83,6 +84,13 @@ function _M:event(e)
 	elseif e.se == "UserInfo" then
 		local info = e.data:unserialize()
 		if not info then return end
+	elseif e.se == "ChannelList" then
+		local info = zlib.decompress(e.data):unserialize()
+		if not info then return end
+		for _, user in ipairs(info.users) do
+			print(" === ", user.name, user.cur_char and user.cur_char.title, user.cur_char and user.cur_char.valid, user.cur_char and user.cur_char.module)
+		end
+		self.channels_changed = true
 	end
 end
 
@@ -93,6 +101,7 @@ function _M:join(channel)
 	self.channels[channel] = self.channels[channel] or {users={}, log={}}
 	self.channels_changed = true
 	self.changed = true
+	self:updateChanList(true)
 end
 
 function _M:selectChannel(channel)
@@ -100,6 +109,7 @@ function _M:selectChannel(channel)
 	self.cur_channel = channel
 	self.channels_changed = true
 	self.changed = true
+	self:updateChanList(true)
 end
 
 function _M:talk(msg)
@@ -124,8 +134,17 @@ function _M:talkBox()
 	end)
 	d.key:addBind("EXIT", function() game:unregisterDialog(d) end)
 	game:registerDialog(d)
+
+	self:updateChanList()
 end
 
+function _M:updateChanList(force)
+	local time = core.game.getTime()
+	if force or not self.last_chan_update or self.last_chan_update + 60000 < time then
+		self.last_chan_update = time
+		core.profile.pushOrder(string.format("o='ChatChannelList' channel=%q", self.cur_channel))
+	end
+end
 
 ----------------------------------------------------------------
 -- UI Section
@@ -162,15 +181,7 @@ function _M:resize(x, y, w, h, fontname, fontsize, color, bgcolor)
 		self.bg_texture, self.bg_texture_w, self.bg_texture_h = self.bg_surface:glTexture()
 	end
 
-	local sb, sb_w, sb_h = self:getImage("ui/scrollbar.png")
-	local ssb, ssb_w, ssb_h = self:getImage("ui/scrollbar-sel.png")
-
-	self.scrollbar = { bar = {}, sel = {} }
-	self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.tex, self.scrollbar.sel.texw, self.scrollbar.sel.texh = ssb_w, ssb_h, ssb:glTexture()
-	local s = core.display.newSurface(sb_w, self.h)
-	s:erase(200,0,0)
-	for i = 0, self.h do s:merge(sb, 0, i) end
-	self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.tex, self.scrollbar.bar.texw, self.scrollbar.bar.texh = ssb_w, self.h, s:glTexture()
+	self.scrollbar = Slider.new{size=self.h - 20, max=1, inverse=true}
 
 	self.mouse = Mouse.new()
 	self.mouse.delegate_offset_x = self.display_x
@@ -188,6 +199,8 @@ function _M:resize(x, y, w, h, fontname, fontsize, color, bgcolor)
 			if last_ok then self:selectChannel(last_ok.name) end
 		end
 	end)
+
+	self.last_chan_update = 0
 end
 
 function _M:display()
@@ -262,10 +275,9 @@ function _M:toScreen()
 	end
 
 	if true then
-		local pos = self.scroll * (self.h - self.fh) / (self.max - self.max_display + 1)
-
-		self.scrollbar.bar.tex:toScreenFull(self.display_x + self.w - self.scrollbar.bar.w, self.display_y, self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.texw, self.scrollbar.bar.texh)
-		self.scrollbar.sel.tex:toScreenFull(self.display_x + self.w - self.scrollbar.sel.w, self.display_y + self.h - self.scrollbar.sel.h - pos, self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.texw, self.scrollbar.sel.texh)
+		self.scrollbar.pos = self.scroll
+		self.scrollbar.max = self.max - self.max_display + 1
+		self.scrollbar:display(self.display_x + self.w - self.scrollbar.w, self.display_y)
 	end
 end
 
diff --git a/game/engines/default/engine/ui/Slider.lua b/game/engines/default/engine/ui/Slider.lua
index b4147e988d7b9105d6a50892e2f92b829467d097..7a41e9e880ec19f5e291bc751774ffac6db3deba 100644
--- a/game/engines/default/engine/ui/Slider.lua
+++ b/game/engines/default/engine/ui/Slider.lua
@@ -27,6 +27,7 @@ function _M:init(t)
 	self.size = assert(t.size, "no slider size")
 	self.max = assert(t.max, "no slider max")
 	self.pos = t.pos or 0
+	self.inverse = t.inverse
 
 	Base.init(self, t)
 end
@@ -43,5 +44,11 @@ function _M:display(x, y)
 	self.top.t:toScreenFull(x, y, self.top.w, self.top.h, self.top.tw, self.top.th)
 	self.bottom.t:toScreenFull(x, y + self.h - self.bottom.h, self.bottom.w, self.bottom.h, self.bottom.tw, self.bottom.th)
 	self.middle.t:toScreenFull(x, y + self.top.h, self.middle.w, self.h - self.top.h - self.bottom.h, self.middle.tw, self.middle.th)
-	self.sel.t:toScreenFull(x - (self.sel.w - self.top.w) / 2, y + (self.pos * self.size / self.max) + self.sel.h / 2, self.sel.w, self.sel.h, self.sel.tw, self.sel.th)
+	self.pos = util.bound(self.pos, 0, self.max)
+	if self.inverse then
+		y = y + self.h - (self.pos * self.size / self.max) + self.sel.h / 2
+	else
+		y = y + (self.pos * self.size / self.max) + self.sel.h / 2
+	end
+	self.sel.t:toScreenFull(x - (self.sel.w - self.top.w) / 2, y, self.sel.w, self.sel.h, self.sel.tw, self.sel.th)
 end
diff --git a/game/engines/default/modules/boot/class/Game.lua b/game/engines/default/modules/boot/class/Game.lua
index ca0d37a1b2b04341bd4174f78553a523222e4ca4..06b67f5e49fb14133a8022383b4d3391ab3bda6f 100644
--- a/game/engines/default/modules/boot/class/Game.lua
+++ b/game/engines/default/modules/boot/class/Game.lua
@@ -64,6 +64,7 @@ function _M:init()
 	end
 
 	self:loaded()
+	profile:currentCharacter("Main Menu", "Main Menu")
 end
 
 function _M:loaded()
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index ec22dd71212abbfe54480aeffbfa9fc628b39f81..dd318648045a0d159b651b6ee90aff71933d240e 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1376,6 +1376,7 @@ function _M:levelup()
 			self.easy_mode_lifes = (self.easy_mode_lifes or 0) + 1
 			game.logPlayer(self, "#AQUAMARINE#You have gained one more life (%d remaining).", self.easy_mode_lifes)
 		end
+		game:updateCurrentChar()
 	end
 end
 
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 28b6e408044c5390758ed6118666dee56f86cb69..aa1c3d8a5b14d23a862dacc8cd67483e370b0191 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -164,6 +164,7 @@ function _M:newGame()
 
 		-- Register the character online if possible
 		self.player:getUUID()
+		self:updateCurrentChar()
 	end
 
 	-- Load for quick birth
@@ -313,6 +314,7 @@ function _M:loaded()
 
 	if self.always_target then Map:setViewerFaction(self.player.faction) end
 	if self.player and config.settings.cheat then self.player.__cheated = true end
+	self:updateCurrentChar()
 end
 
 function _M:setupDisplayMode(reboot, mode)
@@ -384,6 +386,12 @@ function _M:save()
 	return class.save(self, self:defaultSavedFields{difficulty=true, to_re_add_actors=true, party=true, _chronoworlds=true}, true)
 end
 
+function _M:updateCurrentChar()
+	if not self.party then return end
+	local player = self.party:findMember{main=true}
+	profile:currentCharacter(self.__mod_info.name, ("%s the level %d %s %s"):format(player.name, player.level, player.descriptor.subrace, player.descriptor.subclass))
+end
+
 function _M:getSaveDescription()
 	local player = self.party:findMember{main=true}
 
diff --git a/game/profile-thread/Client.lua b/game/profile-thread/Client.lua
index 57fef177f8fa645df3c765fbcad697ee447a1db3..4050e1cb554f86ce741adc0524ae572e8eab7892 100644
--- a/game/profile-thread/Client.lua
+++ b/game/profile-thread/Client.lua
@@ -127,6 +127,7 @@ function _M:login()
 			self.auth = self.last_line:unserialize()
 			cprofile.pushEvent(string.format("e='Auth' ok=%q", self.last_line))
 			self:connectedPull()
+			if self.cur_char then self:orderCurrentCharacter(self.cur_char) end
 			return true
 		else
 			print("[PROFILE] could not log in")
@@ -184,7 +185,7 @@ end
 
 function _M:handleOrder(o)
 	o = o:unserialize()
-	if not self.sock and o.o ~= "Login" then return end -- Dont do stuff without a connection, unless we try to auth
+	if not self.sock and o.o ~= "Login" and o.o ~= "CurrentCharacter" then return end -- Dont do stuff without a connection, unless we try to auth
 	if self["order"..o.o] then self["order"..o.o](self, o) end
 end
 
@@ -291,6 +292,11 @@ function _M:orderSaveChardump(o)
 	cprofile.pushEvent("e='SaveChardump' ok=true")
 end
 
+function _M:orderCurrentCharacter(o)
+	self:command("CHAR", "CUR", table.serialize(o))
+	self.cur_char = o
+end
+
 function _M:orderChatTalk(o)
 	self:command("BRDC", o.channel, o.msg)
 	self:read("200")
@@ -312,6 +318,17 @@ function _M:orderChatUserInfo(o)
 	end
 end
 
+function _M:orderChatChannelList(o)
+	self:command("CLST", o.channel)
+	if self:read("200") then
+		local _, _, size = self.last_line:find("^([0-9]+)")
+		size = tonumber(size)
+		if not size or size < 1 then return end
+		local body = self:receive(size)
+		cprofile.pushEvent(string.format("e='Chat' se='ChannelList' channel=%q data=%q", o.channel, body))
+	end
+end
+
 --------------------------------------------------------------------
 -- Pushes comming from the push socket
 --------------------------------------------------------------------