Skip to content

Commit

Permalink
docs: add multiplayer mode
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenybai committed Oct 24, 2023
1 parent f806a08 commit a723863
Show file tree
Hide file tree
Showing 12 changed files with 462 additions and 179 deletions.
340 changes: 169 additions & 171 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions website/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.next
node_modules
.env
31 changes: 31 additions & 0 deletions website/components/cursor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export function Cursor({
color,
x,
y,
}: {
color: string;
x: number;
y: number;
}) {
return (
<svg
style={{
position: 'absolute',
left: 0,
top: 0,
transform: `translateX(${x}px) translateY(${y}px)`,
zIndex: 1000,
}}
width="24"
height="36"
viewBox="0 0 24 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.65376 12.3673H5.46026L5.31717 12.4976L0.500002 16.8829L0.500002 1.19841L11.7841 12.3673H5.65376Z"
fill={color}
/>
</svg>
);
}
23 changes: 22 additions & 1 deletion website/components/extra-content.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import dynamic from 'next/dynamic';
import { startTransition, useEffect, useRef, useState } from 'react';
import { CarbonAds } from './ad';
import { getCount } from './live';

const LagRadar = dynamic(() => import('react-lag-radar'), { ssr: false });

export function ExtraContent() {
return (
<>
<Showdown />
<Status />
<CarbonAds />
</>
);
}

export function Status() {
const [, forceUpdate] = useState({});
const count = getCount();
// useEffect(() => {
// const interval = setInterval(() => {
// forceUpdate({});
// }, 1000);
// return () => clearInterval(interval);
// }, []);
return (
<div className="flex ml-1 items-center gap-2 text-black dark:text-white">
<div className="relative flex h-3 w-3">
<div className="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-400 opacity-75"></div>
<div className="relative inline-flex rounded-full h-3 w-3 bg-purple-500"></div>
</div>
{count} users connected.
</div>
);
}

// million-ignore
export function Showdown({ initStart = false, amount = 1000 }) {
const [start, setStart] = useState<boolean>(false);
Expand Down
136 changes: 136 additions & 0 deletions website/components/live.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import {
RoomProvider,
useOthers,
useUpdateMyPresence,
} from '../liveblocks.config';
import { Cursor } from './cursor';

const COLORS = [
'#E57373',
'#9575CD',
'#4FC3F7',
'#81C784',
'#FFF176',
'#FF8A65',
'#F06292',
'#7986CB',
];

function useLiveCursors() {
const updateMyPresence = useUpdateMyPresence();

useEffect(() => {
const scroll = {
x: window.scrollX,
y: window.scrollY,
};

let lastPosition: { x: number; y: number } | null = null;

function transformPosition(cursor: { x: number; y: number }) {
return {
x: cursor.x / window.innerWidth,
y: cursor.y,
};
}

function onPointerMove(event: PointerEvent) {
event.preventDefault();
const position = {
x: event.pageX,
y: event.pageY,
};
lastPosition = position;
updateMyPresence({
cursor: transformPosition(position),
});
}

function onPointerLeave() {
lastPosition = null;
updateMyPresence({ cursor: null });
}

function onDocumentScroll() {
if (lastPosition) {
const offsetX = window.scrollX - scroll.x;
const offsetY = window.scrollY - scroll.y;
const position = {
x: lastPosition.x + offsetX,
y: lastPosition.y + offsetY,
};
lastPosition = position;
updateMyPresence({
cursor: transformPosition(position),
});
}
scroll.x = window.scrollX;
scroll.y = window.scrollY;
}

document.addEventListener('scroll', onDocumentScroll);
document.addEventListener('pointermove', onPointerMove);
document.addEventListener('pointerleave', onPointerLeave);

return () => {
document.removeEventListener('scroll', onDocumentScroll);
document.removeEventListener('pointermove', onPointerMove);
document.removeEventListener('pointerleave', onPointerLeave);
};
}, [updateMyPresence]);

const others = useOthers();

const cursors: {
x: number;
y: number;
connectionId: number;
}[] = [];

for (const { connectionId, presence } of others) {
if (presence.cursor) {
cursors.push({
x: presence.cursor.x * window.innerWidth,
y: presence.cursor.y,
connectionId,
});
}
}

return cursors;
}

export function Cursors() {
const cursors = useLiveCursors();

return cursors.map(({ x, y, connectionId }) => (
<Cursor
key={connectionId}
color={COLORS[connectionId % COLORS.length]}
x={x}
y={y}
/>
));
}

export function getCount() {
const others = useOthers();
return others.length;
}

export function LiveProvider({ children }) {
const { pathname } = useRouter();
return (
<RoomProvider
id={`million-${pathname}`}
initialPresence={{
cursor: null,
}}
>
{children}
<Cursors />
</RoomProvider>
);
}
14 changes: 14 additions & 0 deletions website/liveblocks.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createClient } from '@liveblocks/client';
import { createRoomContext } from '@liveblocks/react';
import type { JsonObject} from '@liveblocks/client';

const client = createClient({
throttle: 16,
publicApiKey: process.env.NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY!,
});
interface Presence extends JsonObject {
cursor: { x: number; y: number } | null;
}

export const { RoomProvider, useOthers, useMyPresence, useUpdateMyPresence } =
createRoomContext<Presence>(client);
8 changes: 5 additions & 3 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"@babel/traverse": "^7.21.5",
"@codesandbox/sandpack-react": "^2.6.7",
"@headlessui/react": "^1.7.13",
"@liveblocks/client": "^1.5.0",
"@liveblocks/react": "^1.5.0",
"@loglib/tracker": "^0.5.0",
"@tailwindcss/line-clamp": "^0.4.4",
"@vercel/analytics": "^1.0.0",
Expand All @@ -23,9 +25,9 @@
"dedent": "^0.7.0",
"framer-motion": "^10.12.4",
"million": "workspace:*",
"next": "^13.4.19",
"nextra": "^2.12.3",
"nextra-theme-docs": "^2.12.3",
"next": "^13.5.6",
"nextra": "^2.13.2",
"nextra-theme-docs": "^2.13.2",
"postcss": "^8.4.21",
"react": "^18.2.0",
"react-card-flip": "^1.2.0",
Expand Down
11 changes: 7 additions & 4 deletions website/pages/_app.mdx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import '../styles/global.css';
import { Analytics } from '@vercel/analytics/react';
import { Inter } from 'next/font/google';
import { LiveProvider } from '../components/live';

export const inter = Inter({ subsets: ['latin'] });

export default function App({ Component, pageProps }) {
return (
<main className={inter.className}>
<Component {...pageProps} />
<Analytics />
</main>
<LiveProvider>
<main className={inter.className}>
<Component {...pageProps} />
<Analytics />
</main>
</LiveProvider>
);
}
1 change: 1 addition & 0 deletions website/pages/blog/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"typesetting": "article"
}
},
"wyze": "Case Study: Wyze",
"million-hacktoberfest-celebration": "Million.js Hacktoberfest: Join the Celebration!",
"million-beyond-speed": "Million Beyond 'Speed'",
"million-v2.5.3": "Million.js v2.5.3",
Expand Down
58 changes: 58 additions & 0 deletions website/pages/blog/wyze.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: 'Case Study: Wyze'
date: OCT 19, 2023
description: How Million.js helped Wyze speed up the Web View product by 8x in less than a week.
---

import Image from 'next/image';

<div className="flex justify-center mt-6">
<a href="https://wyze.com">
<Image src="/wyze-banner.png" width={500} height={130} />
</a>
</div>

<div className="flex flex-col items-center gap-4 text-center">

# Case Study: Wyze

<p>
How Million.js helped Wyze speed up the Web View product by 8x in less than a
week.
</p>

<small>[AIDEN BAI](https://aidenybai.com) OCT 19 2023</small>
</div>

Introduction
Wyze.com, a platform renowned for its smart home products, has always been on the lookout for cutting-edge technologies to enhance user interaction and engagement on its web view product. In its quest for a robust, performant, and user-friendly web view, Wyze.com collaborated with Million.js, a lightweight virtual DOM library.

Challenges
Wyze's web view product was facing challenges with rendering speed and efficiency which was impacting the user experience. The existing technology stack was not adept at handling high-frequency updates and rendering large lists of dynamic content.

The primary challenge was to find a solution that could significantly enhance rendering performance without compromising on the usability and the existing features of the web view product. Moreover, the solution needed to be lightweight and easy to integrate with the existing technology stack.

Requirements

Boost rendering performance significantly.
Ensure a lightweight solution to not burden the existing infrastructure.
Easy integration with the existing technology stack.
Maintain or enhance the current level of usability and features.
Solution and Implementation
Million.js emerged as a promising solution to Wyze.com's challenges. It's a lightweight virtual DOM library that provides high-performance rendering, making it a perfect match for the requirements.

The implementation process kicked off with a thorough analysis and benchmarking to understand the performance gaps and set clear performance goals. The Wyze.com’s development team, along with the Million.js community, worked in tandem to devise a robust implementation plan.

The integration of Million.js was smooth, and it was customized to align with the Wyze.com's web view product's architecture. The Million.js library was adept at handling high-frequency updates and rendering large lists of dynamic content efficiently.

During the implementation, several performance optimization techniques were also employed to further boost the rendering speed. This included optimizing the existing codebase to take full advantage of Million.js's virtual DOM diffing and patching capabilities.

Results
The integration of Million.js significantly improved the rendering performance of Wyze.com’s web view product. The rendering times were reduced drastically which contributed to a smoother and more responsive user experience.

Furthermore, the lightweight nature of Million.js ensured that the web view remained highly performant even under heavy loads, showcasing its capability to handle high-frequency updates efficiently.

The collaboration with Million.js not only addressed the performance challenges but also laid a strong foundation for future enhancements. The success of this integration has spurred Wyze.com to explore further optimizations and additional features to continue enhancing the user experience.

Conclusion
The case study exemplifies how a focused and collaborative effort between Wyze.com and Million.js led to significant performance improvements. The integration of Million.js proved to be a decisive step towards achieving a superior, user-friendly, and performant web view product on Wyze.com. This collaboration underscores the potential of lightweight virtual DOM libraries like Million.js in addressing performance challenges and enhancing the user experience in web-based applications.
Binary file added website/public/wyze-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions website/styles/global.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer utilities {
.pause {
animation-play-state: paused;
Expand Down Expand Up @@ -73,6 +74,23 @@ html[class~='dark'] .gradient-text {
animation-iteration-count: 1;
}

.animate-ping {
-webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
}

@keyframes ping {
0% {
transform: scale(1);
opacity: 1;
}
75%,
100% {
transform: scale(2);
opacity: 0;
}
}

.svg {
width: 100%;
height: 100%;
Expand Down

2 comments on commit a723863

@vercel
Copy link

@vercel vercel bot commented on a723863 Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

million-kitchen-sink – ./packages/kitchen-sink

million-kitchen-sink.vercel.app
million-kitchen-sink-git-main-millionjs.vercel.app
million-kitchen-sink-millionjs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on a723863 Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

sink – ./packages/kitchen-sink

sink-git-main-millionjs.vercel.app
sink.million.dev
sink-millionjs.vercel.app
million-kitchen-sink-atit.vercel.app

Please sign in to comment.