diff --git a/lang-trials/scala-benchmark/.gitignore b/lang-trials/scala-benchmark/.gitignore
new file mode 100644
index 0000000..9f2a453
--- /dev/null
+++ b/lang-trials/scala-benchmark/.gitignore
@@ -0,0 +1,2 @@
+target
+sbt.json
\ No newline at end of file
diff --git a/lang-trials/scala-benchmark/build.sbt b/lang-trials/scala-benchmark/build.sbt
new file mode 100644
index 0000000..507885d
--- /dev/null
+++ b/lang-trials/scala-benchmark/build.sbt
@@ -0,0 +1,9 @@
+enablePlugins(ScalaJSPlugin)
+
+name := "Circular Law"
+scalaVersion := "3.4.2"
+scalaJSUseMainModuleInitializer := true
+
+libraryDependencies += "com.raquo" %%% "laminar" % "17.0.0"
+libraryDependencies += "ai.dragonfly" %%% "slash" % "0.3.1"
+libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.8.0"
diff --git a/lang-trials/scala-benchmark/index.html b/lang-trials/scala-benchmark/index.html
new file mode 100644
index 0000000..f9eb11c
--- /dev/null
+++ b/lang-trials/scala-benchmark/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ The circular law
+
+
+
+
+
diff --git a/lang-trials/scala-benchmark/main.css b/lang-trials/scala-benchmark/main.css
new file mode 100644
index 0000000..a89c3bc
--- /dev/null
+++ b/lang-trials/scala-benchmark/main.css
@@ -0,0 +1,22 @@
+body {
+ margin-left: 20px;
+ margin-top: 20px;
+ color: #fcfcfc;
+ background-color: #202020;
+}
+
+#app {
+ display: flex;
+ flex-direction: column;
+ width: 600px;
+}
+
+canvas {
+ float: left;
+ background-color: #020202;
+ border-radius: 10px;
+}
+
+input {
+ margin-top: 5px;
+}
\ No newline at end of file
diff --git a/lang-trials/scala-benchmark/project/build.properties b/lang-trials/scala-benchmark/project/build.properties
new file mode 100644
index 0000000..ee4c672
--- /dev/null
+++ b/lang-trials/scala-benchmark/project/build.properties
@@ -0,0 +1 @@
+sbt.version=1.10.1
diff --git a/lang-trials/scala-benchmark/project/plugins.sbt b/lang-trials/scala-benchmark/project/plugins.sbt
new file mode 100644
index 0000000..e5b2699
--- /dev/null
+++ b/lang-trials/scala-benchmark/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")
diff --git a/lang-trials/scala-benchmark/src/main/scala/CircularLawApp.scala b/lang-trials/scala-benchmark/src/main/scala/CircularLawApp.scala
new file mode 100644
index 0000000..2f74cd3
--- /dev/null
+++ b/lang-trials/scala-benchmark/src/main/scala/CircularLawApp.scala
@@ -0,0 +1,91 @@
+import com.raquo.laminar.api.L.{*, given}
+import narr.*
+import org.scalajs.dom
+import org.scalajs.dom.document
+import scala.math.{cos, sin}
+import slash.matrix.Matrix
+import slash.matrix.decomposition.Eigen
+
+object CircularLawApp:
+ val canvas = canvasTag(widthAttr := 600, heightAttr := 600)
+ val ctx = canvas.ref.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D]
+
+ val (eigvalSeries, runTimeReport) = randomEigvalSeries[60]()
+ val timeVar = Var("0")
+
+ def draw(timeStr: String): Unit =
+ // center and normalize the coordinate system
+ val width = canvas.ref.width
+ val height = canvas.ref.height
+ ctx.setTransform(1d, 0d, 0d, -1d, 0.5*width, 0.5*height)
+
+ // clear the previous frame
+ ctx.clearRect(-0.5*width, -0.5*width, width, height)
+
+ // find the resolution
+ val rDisp: Double = 1.5
+ val res = width / (2*rDisp)
+
+ // draw the eigenvalues
+ val eigvals = eigvalSeries(timeStr.toInt)
+ for n <- 0 to eigvals(0).length-1 do
+ ctx.beginPath()
+ ctx.arc(
+ res * eigvals(0)(n),
+ res * eigvals(1)(n),
+ 3d,
+ 0d, 2*math.Pi
+ )
+ ctx.fill()
+
+ def eigvalsRotated[N <: Int](A: Matrix[N, N], time: Double)(using ValueOf[N]): (NArray[Double], NArray[Double]) =
+ // create transformation
+ val maxFreq = 4
+ val T = Matrix.identity[N, N]
+ val dim: Int = valueOf[N]
+ for n <- 0 to dim by 2 do
+ val a = cos(math.Pi * time * (n % maxFreq))
+ val b = sin(math.Pi * time * (n % maxFreq))
+ T(n, n) = a
+ T(n+1, n) = b
+ T(n, n+1) = -b
+ T(n+1, n+1) = a
+
+ // find eigenvalues
+ val eigen = Eigen(T*A)
+ (
+ eigen.realEigenvalues.asInstanceOf[NArray[Double]],
+ eigen.imaginaryEigenvalues.asInstanceOf[NArray[Double]]
+ )
+
+ def randomEigvalSeries[N <: Int]()(using ValueOf[N]): (List[(NArray[Double], NArray[Double])], String) =
+ val timeRes = 100
+ val dim: Int = valueOf[N]
+ val startTime = System.currentTimeMillis()
+ val A = new Matrix[N, N](
+ NArray.tabulate(dim*dim)(k => (math.E*k*k) % 2 - 1)
+ ).times(math.sqrt(3d / dim))
+ val series = List.tabulate(timeRes)(t => eigvalsRotated(A, t.toDouble / timeRes))
+ val endTime = System.currentTimeMillis()
+ val runTime = endTime - startTime
+ (series, runTime.toString() + " ms")
+
+ def main(args: Array[String]): Unit =
+ ctx.fillStyle = "white"
+
+ lazy val app = div(
+ idAttr := "app",
+ div(runTimeReport),
+ canvas,
+ input(
+ typ := "range",
+ minAttr := "0",
+ maxAttr := (eigvalSeries.length-1).toString,
+ controlled(
+ value <-- timeVar.signal,
+ onInput.mapToValue --> timeVar.writer
+ ),
+ timeVar.signal --> draw
+ )
+ )
+ renderOnDomContentLoaded(document.body, app)