diff --git a/Gemfile b/Gemfile
index 2712516251dafd4a5ae7e8615e086d87461354cd..4717b9b16532d4a2ae157e1ddcad4cade2dab9d4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -61,3 +61,7 @@ gem "solid_queue"
 gem "httparty"
 
 gem "tailwindcss-rails", "~> 2.6"
+
+gem "hotwire-rails", "~> 0.1.3"
+
+gem "importmap-rails", "~> 2.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 6d767c6db4d900a73468eb7a8b094116f8e02695..a07fabb1b1449192b9db3d215e5a0f70c3d70bac 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -108,11 +108,19 @@ GEM
       raabro (~> 1.4)
     globalid (1.2.1)
       activesupport (>= 6.1)
+    hotwire-rails (0.1.3)
+      rails (>= 6.0.0)
+      stimulus-rails
+      turbo-rails
     httparty (0.21.0)
       mini_mime (>= 1.0.0)
       multi_xml (>= 0.5.2)
     i18n (1.14.4)
       concurrent-ruby (~> 1.0)
+    importmap-rails (2.0.1)
+      actionpack (>= 6.0.0)
+      activesupport (>= 6.0.0)
+      railties (>= 6.0.0)
     io-console (0.7.2)
     irb (1.12.0)
       rdoc
@@ -229,6 +237,8 @@ GEM
     sqlite3 (1.7.3-aarch64-linux)
     sqlite3 (1.7.3-arm64-darwin)
     sqlite3 (1.7.3-x86_64-linux)
+    stimulus-rails (1.3.3)
+      railties (>= 6.0.0)
     stringio (3.1.0)
     strscan (3.1.0)
     tailwindcss-rails (2.6.0-aarch64-linux)
@@ -239,6 +249,10 @@ GEM
       railties (>= 7.0.0)
     thor (1.3.1)
     timeout (0.4.1)
+    turbo-rails (2.0.5)
+      actionpack (>= 6.0.0)
+      activejob (>= 6.0.0)
+      railties (>= 6.0.0)
     tzinfo (2.0.6)
       concurrent-ruby (~> 1.0)
     web-console (4.2.1)
@@ -264,7 +278,9 @@ DEPENDENCIES
   bootsnap
   capybara
   debug
+  hotwire-rails (~> 0.1.3)
   httparty
+  importmap-rails (~> 2.0)
   jbuilder
   puma (>= 5.0)
   rails (~> 7.1.2)
diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js
index 338a0e82666ddaaf6f02347084fff71f72fd4604..d39ca5501f1720c4179c2159e2c38bccf2016e50 100644
--- a/app/assets/config/manifest.js
+++ b/app/assets/config/manifest.js
@@ -1,3 +1,5 @@
 //= link_tree ../images
 //= link_directory ../stylesheets .css
 //= link_tree ../builds
+//= link_tree ../../javascript .js
+//= link_tree ../../../vendor/javascript .js
diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..820907634111ecd4ae7098e5dbd307f5bc43384a
--- /dev/null
+++ b/app/controllers/assignments_controller.rb
@@ -0,0 +1,8 @@
+class AssignmentsController < ApplicationController
+  def index
+    @assignments = Assignment.all
+    if params[:user_id]
+      @assignments = @assignments.where(user_id: params[:user_id])
+    end
+  end
+end
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index 3614abd3e656e217591a777b41072e7443d78deb..038e5903b44193d0af9e09a34410cd2454bfc5da 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -7,5 +7,6 @@ class ConferencesController < ApplicationController
     @conference = Conference.find_by(slug: params[:slug])
     @sessions = @conference.sessions.where.not(starts_at: nil).includes(:stage).order(:starts_at)
     @sessions_by_date = @sessions.group_by{ |x| x.starts_at.to_date }
+    @users = User.all
   end
 end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 47fa91b9d1809af607fc10f6764667136dcb553f..5f107b097892485cbbb6052bac926a8008030df0 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -16,4 +16,44 @@ class SessionsController < ApplicationController
 
     # Further filtering options can be added here
   end
+
+  def show
+    @conference = Conference.find_by(slug: params[:slug])
+    @session = Session.includes(:stage).find_by(ref_id: params[:ref_id])
+    @users = User.all
+  end
+
+  def assign_user
+    @session = Session.find_by(ref_id: params[:ref_id])
+    @conference = Conference.find_by(slug: params[:slug])
+    @user = User.find(params[:user_id])
+    @assignment = @session.assignments.new(session: @session, user: @user)
+    @users = User.all
+
+    if @assignment.save
+      flash.now[:success] = 'User assigned successfully.'
+      respond_to do |format|
+        # format.turbo_stream
+        format.html { redirect_to conference_session_path(@session.conference, @session), success: 'User assigned successfully.' }
+      end
+    else
+      flash.now[:alert] = 'Failed to assign user.'
+      respond_to do |format|
+        # format.turbo_stream { render :show, status: :unprocessable_entity }
+        format.html { render :show, status: :unprocessable_entity, alert: 'Failed to assign user.' }
+      end
+    end
+  end
+
+  def unassign_user
+    @session = Session.find_by(ref_id: params[:ref_id])
+    @user = User.find(params[:user_id])
+    @assignment = Assignment.find_by(session: @session, user: @user)
+
+    if @assignment&.destroy
+      redirect_to conference_session_path(@session.conference, @session), notice: 'User removed successfully.'
+    else
+      redirect_to conference_session_path(@session.conference, @session), alert: 'Failed to remove user.'
+    end
+  end
 end
diff --git a/app/helpers/assignments_helper.rb b/app/helpers/assignments_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6f7c33b927756135ddc70aa905e5778d1f41db6b
--- /dev/null
+++ b/app/helpers/assignments_helper.rb
@@ -0,0 +1,2 @@
+module AssignmentsHelper
+end
diff --git a/app/javascript/application.js b/app/javascript/application.js
new file mode 100644
index 0000000000000000000000000000000000000000..e239947abfd19eb083fb16ac650b9a49b63ff6e4
--- /dev/null
+++ b/app/javascript/application.js
@@ -0,0 +1,3 @@
+// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
+import "controllers"
+import "@hotwired/turbo-rails"
diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js
new file mode 100644
index 0000000000000000000000000000000000000000..1213e85c7ac805be18b4ea04f93ab028cc975255
--- /dev/null
+++ b/app/javascript/controllers/application.js
@@ -0,0 +1,9 @@
+import { Application } from "@hotwired/stimulus"
+
+const application = Application.start()
+
+// Configure Stimulus development experience
+application.debug = false
+window.Stimulus   = application
+
+export { application }
diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js
new file mode 100644
index 0000000000000000000000000000000000000000..5975c0789d7cf1cc918034ba31a8f102e06e93b9
--- /dev/null
+++ b/app/javascript/controllers/hello_controller.js
@@ -0,0 +1,7 @@
+import { Controller } from "@hotwired/stimulus"
+
+export default class extends Controller {
+  connect() {
+    this.element.textContent = "Hello World!"
+  }
+}
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..54ad4cad4d448fb5598447916ac5535d03376761
--- /dev/null
+++ b/app/javascript/controllers/index.js
@@ -0,0 +1,11 @@
+// Import and register all your controllers from the importmap under controllers/*
+
+import { application } from "controllers/application"
+
+// Eager load all controllers defined in the import map under controllers/**/*_controller
+import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
+eagerLoadControllersFrom("controllers", application)
+
+// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
+// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
+// lazyLoadControllersFrom("controllers", application)
diff --git a/app/models/assignment.rb b/app/models/assignment.rb
index e8bb8a3b18409ebf8444e4842f1056f0c68fd300..b6db13245d0af1ae3e1810ba1f139c4eb2cd8137 100644
--- a/app/models/assignment.rb
+++ b/app/models/assignment.rb
@@ -1,4 +1,6 @@
 class Assignment < ApplicationRecord
   belongs_to :user
   belongs_to :session
+
+  validates :user_id, uniqueness: { scope: :session_id, message: "has already been assigned to this session" }
 end
diff --git a/app/models/conference.rb b/app/models/conference.rb
index 07e4145c189bd467423e151d072b1b81eae38589..40b40a551f68f9104764dd76c72882b6f9b35639 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -6,4 +6,8 @@ class Conference < ApplicationRecord
   def days
     (starts_at.to_date..ends_at.to_date)
   end
+
+  def to_param
+    slug
+  end
 end
diff --git a/app/models/session.rb b/app/models/session.rb
index 687e2268d68f9f74905269f243ac01480ec500a8..6a18ee751259d3064d9d318f6441cfaa2cf656c3 100644
--- a/app/models/session.rb
+++ b/app/models/session.rb
@@ -5,4 +5,8 @@ class Session < ApplicationRecord
   has_many :users, through: :assignments
 
   validates :ref_id, uniqueness: { scope: :conference_id }
+
+  def to_param
+    ref_id
+  end
 end
diff --git a/app/views/assignments/index.html.erb b/app/views/assignments/index.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..139f9496ed637b6d4901a8d2cd221ea0caff9c2c
--- /dev/null
+++ b/app/views/assignments/index.html.erb
@@ -0,0 +1,10 @@
+<div>
+  <% @assignments.group_by(&:user).each do |user, assignments| %>
+  <h4><%= user.name %></h4>
+  <ul>
+  <% assignments.each do |assignment| %>
+    <li><%= link_to assignment.session.title, assignment.session %></li>
+  <% end %>
+  </ul>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/app/views/assignments/show.html.erb b/app/views/assignments/show.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..6c530750c9b9ffc4b68ac98681587d7fb112c510
--- /dev/null
+++ b/app/views/assignments/show.html.erb
@@ -0,0 +1,4 @@
+<div>
+  <h1 class="font-bold text-4xl">Assignments#show</h1>
+  <p>Find me in app/views/assignments/show.html.erb</p>
+</div>
diff --git a/app/views/conferences/show.html.erb b/app/views/conferences/show.html.erb
index 3c732431fc288417fa3f0baa1766243704730d94..911fe41065d47a9e9ff231e29a362086281b2b3f 100644
--- a/app/views/conferences/show.html.erb
+++ b/app/views/conferences/show.html.erb
@@ -52,10 +52,15 @@ current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance
           <h4><%= stage.name %></h4>
           <div class="stage-sessions">
           <% sessions.each do |session| %>
-            <div class="session" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour%>px;">
-              <h4><%= session.title %></h4>
+            <div class="session" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour%>px; overflow: scroll;">
+              <h4><%= link_to session.title, [@conference, session] %></h4>
               <p class="session-time"><%= session.starts_at.strftime('%H:%M') %> - <%= session.ends_at.strftime('%H:%M') %></p>
-              <div class="session-desc"><%= session.description.html_safe %></div>
+              <%#<div class="session-desc"><%= session.description.html_safe %><%#/div>%>
+              <%= form_with url: assign_user_session_path(@conference, session), method: :post, local: true do |f| %>
+                <%= f.label :user_id, "Assign User" %>
+                <%= f.select :user_id, options_from_collection_for_select(@users, :id, :name) %>
+                <%= f.submit "Assign" %>
+              <% end %>
             </div>
           <% end %>
           </div>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index f02f8a20840d3597bc624d0e96212d9b600729b7..9f79c2f38b69d91c34b45ed70fdeae02cdfdee1e 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -8,6 +8,7 @@
     <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
 
     <%= stylesheet_link_tag "application" %>
+    <%= javascript_importmap_tags %>
   </head>
 
   <body>
diff --git a/app/views/sessions/show.html.erb b/app/views/sessions/show.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..32b19c4c919f50510558cad797b20244f437c1d4
--- /dev/null
+++ b/app/views/sessions/show.html.erb
@@ -0,0 +1,34 @@
+<div>
+  <h6><%= @session.conference.name %></h6>
+  <h1><%= @session.title %></h1>
+  <h2><%= @session.language %> <%= @session.is_interpreted ? "(live interpretation)" : "" %></h2>
+  <h2><%= @session.starts_at.strftime("%Y-%m-%d") %> <%= @session.starts_at.strftime("%H:%M") %> &ndash; <%= @session.ends_at.strftime("%H:%M") %></h2>
+  <h2><%= @session.stage.name %>&middot; <%= @session.session_format %> &middot; <%= @session.track %></h2>
+  <p><%= @session.description.html_safe %></p>
+  
+  <h3>Assigned Users</h3>
+  <ul>
+    <% @session.users.each do |user| %>
+      <li>
+        <%= user.name %>
+        <%= link_to '[Remove]', unassign_user_session_path([@session.conference, @session], user_id: user.id), data: { turbo_method: :delete, confirm: 'Are you sure?' } %>
+      </li>
+    <% end %>
+  </ul>
+
+  <%= form_with url: assign_user_session_path([@session.conference, @session]), method: :post, local: true do |f| %>
+    <%= f.label :user_id, "Assign User" %>
+    <%= f.select :user_id, options_from_collection_for_select(@users, :id, :name) %>
+    <%= f.submit "Assign" %>
+    <div id="flash">
+      <% flash.each do |key, value| %>
+        <div class="alert alert-<%= key %>"><%= value %></div>
+      <% end %>
+    </div>
+    <% if @assignment&.errors&.any? %>
+      <div class="alert alert-danger">
+        <%= @assignment.errors.full_messages.join(", ") %>
+      </div>
+    <% end %>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/bin/importmap b/bin/importmap
new file mode 100755
index 0000000000000000000000000000000000000000..36502ab16c7adf42dfc804702966e684d68610dd
--- /dev/null
+++ b/bin/importmap
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+
+require_relative "../config/application"
+require "importmap/commands"
diff --git a/config/importmap.rb b/config/importmap.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cb0480c0c0a7e50463e1bd0e2665a6866ae0435c
--- /dev/null
+++ b/config/importmap.rb
@@ -0,0 +1,7 @@
+# Pin npm packages by running ./bin/importmap
+
+pin "application"
+pin "@hotwired/stimulus", to: "stimulus.min.js"
+pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
+pin_all_from "app/javascript/controllers", under: "controllers"
+pin "@hotwired/turbo-rails", to: "turbo.min.js"
diff --git a/config/routes.rb b/config/routes.rb
index a6369e49458d4141ab49b9e347bc410f2790b026..2521790b545f03b3183b840e6a27e9cc9d694cb3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,5 +1,4 @@
 Rails.application.routes.draw do
-  get 'sessions/index'
   # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
 
   # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
@@ -10,7 +9,20 @@ Rails.application.routes.draw do
   # root "posts#index"
   root "conferences#index"
 
-  resources :conferences, path: '/', param: :slug do
-    resources :sessions, only: [:index]
+  resources :conferences, param: :slug do
+    resources :sessions do
+      resource :assigments
+    end
   end
+
+  resources :assignments
+  resources :sessions, param: :ref_id
+
+  # get 'conferences/:slug', to: 'conferences#show', as: :conference
+  # get 'conferences/:slug/:date', to: 'conferences#show', as: :conference_day
+  # get 'conferences/:slug/session/:ref_id', to: 'sessions#show', as: :conference_session
+  # post 'conferences/:slug/session/:ref_id/assignments', to: 'sessions#assign_user', as: :assign_user_session
+  # delete 'conferences/:slug/session/:ref_id/assignments', to: 'sessions#unassign_user', as: :unassign_user_session
+  # get 'assignments', to: 'assignments#index', as: :assignments
+  # get 'assignments/:user_id', to: 'assignments#index', as: :user_assignments
 end
diff --git a/db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb b/db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3bc825cbcb08cc81c4d6f44903b4c17a07142da6
--- /dev/null
+++ b/db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb
@@ -0,0 +1,5 @@
+class RenameFormatToSessionFormatInSessions < ActiveRecord::Migration[7.1]
+  def change
+    rename_column :sessions, :format, :session_format
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index cb01004b01595856fb00914da91c7720bd1bbfad..eff275dd45355b447281057086c23c2dbcf6140f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.1].define(version: 2024_04_06_184312) do
+ActiveRecord::Schema[7.1].define(version: 2024_05_24_091736) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
@@ -39,7 +39,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_06_184312) do
     t.string "language"
     t.string "status"
     t.text "description"
-    t.string "format"
+    t.string "session_format"
     t.string "track"
     t.boolean "is_interpreted"
     t.datetime "starts_at"
diff --git a/db/seeds.rb b/db/seeds.rb
index 42df632a0a6debcf994906c1081ae757b29892c2..766011bb79a257594a522f844088145c87add8ec 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -29,3 +29,7 @@ Conference.find_or_create_by!(slug: "rp2024") do |c|
   }
   c.import_job_class = "republica_2023_or_later"
 end
+
+User.find_or_create_by!(email: "teal@teal.is") do |user|
+  user.name = "Teal"
+end
diff --git a/test/controllers/assignments_controller_test.rb b/test/controllers/assignments_controller_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e733d3acd7c088c3d8905b7cc94ef6140e6e9be1
--- /dev/null
+++ b/test/controllers/assignments_controller_test.rb
@@ -0,0 +1,8 @@
+require "test_helper"
+
+class AssignmentsControllerTest < ActionDispatch::IntegrationTest
+  test "should get show" do
+    get assignments_show_url
+    assert_response :success
+  end
+end
diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391