Projects
Hide n' Seek is an award-winning, highly-rated browser extension
for Chrome, Edge, and Firefox that lets you hide promoted jobs
and companies on LinkedIn, Indeed, and Glassdoor.
It was awarded the “Featured” badge by the Chrome Web Store for
following best practices in coding, design, user experience,
privacy, and performance.
Being thoughtfully designed, users are treated to an intuitive,
accessible, and delightful experience.
It features device-to-device syncing and is compatible with all
regional versions of LinkedIn, Indeed, and Glassdoor.
How it works
“Block” buttons are added next to every listing. If you see a
listing you don't like, simply click the button to hide it. By
default, the listing will be hidden under an overlay.
Alternatively, you can completely eliminate it from search
results by enabling the “Do not display hidden jobs” option.
To hide promoted jobs, click the “block” button on any promoted
listing and then activate the “Promoted” toggle.
For easy management, you can view a list of everything you've
hidden by clicking the Hide n' Seek button on your browser's
toolbar.
Privacy
Your data remains completely private and is never shared. All
data is stored locally on your device and may sync across your
devices if browser synchronization is enabled.
Download
Edge users: I recommend that you download from the Chrome Web
Store because it releases extension updates much faster than
Edge Add-ons (1–2 days versus 2–4 weeks).
How it was made
Hide n' Seek was written in HTML, CSS, and JavaScript. No
libraries or frameworks were used.
Graphic artwork was drawn with BoxySVG.
The promotional video was created with a combination of Adobe
After Effects and Adobe Premiere Pro.
Check out the
repository
This site is my portfolio. It and
are my first web projects.
Charm's Jobs
The
repository
is available on GitHub.
Originally, my plan was to create a facsimile of
my old resume
and publish it to the web. To accomplish that plan, I started
teaching myself HTML, CSS, and JavaScript.
MDN Web Docs
, an invaluable web developer wiki, soon became my primary
learning resource.
Several months later, I have a resume, a portfolio with two
projects, and cat logos that talk.
The cat logos, which I drew with
Boxy SVG
, are based on artwork by Charlotte Ma. All other SVG artwork,
including the seasonal logo artwork, associated animations, and
paw cursors, is my own work.
The fonts that I use,
Open Sans
and
Comic Neue
, are by Steve Matteson and Craig Rozynski, respectively. The
icons, except for the sun-moon transition icon, are
Material Icons
by Google.
One feature that I built depended on solving cubic parametric
equations (cubic Bézier curves). Solving these equations is
something your browser does every time it renders CSS Animations
or Web Animations, and I needed the same functionality for my
smooth scroller implementation. Instead of implementing my own
root-finding algorithms to solve the equations, I decided to
port
WebKit's algorithm
.
Everything else — the design, the scrolling
systems, the type and talk system, the interactive elements, the
logic,
everything — all down to the smallest detail, was
written with HTML, CSS, and JavaScript. No libraries. No
frameworks.
Features
MomentaMouse — MomentaMouse enables mouse-users to
use touch-style scrolling and flicking gestures to navigate
websites.
Web developers interested in this functionality simply need to
import the module to begin automatically or manually creating
MomentaMouse scrollers.
It has a
repository
and a
demo page
.
Momentum: Faster flicks
produce more momentum, resulting in scrolls that travel
farther and last longer. Slower flicks have the opposite
effect. Flicking in the same direction in quick succession
results in accumulation of scrolling speed.
Overscroll: If a user
tries to scroll beyond a border, the scroll container will
stretch in the direction of the pointer while the pointer is
being held down, but will then rebound when the pointer is let
go.
Bouncing: When the
border of a scroll container is hit by a momentum scroll, the
border will bounce. Harder impacts produce larger bounces.
Highlighting and Dragging:
The touch-screen convention for text-highlighting is to
perform a long-press on a word to highlight it, and then to
drag selection handles, if necessary, to change the selection.
This convention is not necessary with MomentaMouse if a
keyboard is available. Instead, a "Quick Toggle Key," which is
the Ctrl key, is held down to
temporarily deactivate MomentaMouse. This allows you to use
your mouse as you normally would to highlight, copy, or drag
things like text, links, or images. MomentaMouse will
reactivate once the Ctrl key is
let go.
Below is a demonstration of a MomentaMouse scroller along with
controls for adjusting the number of axes, deceleration, and
bounciness.
The Yowl, by Charlotte
Ma, is used as the background image of the two-dimensional
scroll container in this demonstration.
MomentaMouse Demo
MomentaMouse is disabled
Axes
Horizontal
Both
Vertical
Deceleration
None
Minimum
Low
Medium
High
Maximum
Bounciness
None
Minimum
Low
Medium
High
Maximum
Distance
-
Elapsed Time
-
The generation of momentum scrolls is simply an application of
kinematic equations
. After deceleration is set, the only missing variable needed
to calculate scroll duration, distance, and direction is
pointer velocity. The momentum accumulation feature is
accomplished by adjusting a velocity multiplier up or down
depending on the direction and timing criteria between
scrolls.
Scrolling will stop if scroll duration has elapsed, or if one
of the two edges of a one-dimensional scroller has been
reached, or if one of the four vertices of a two-dimensional
scroller has been reached, or if there is a "pointerdown"
event on the scroll container.
Bouncing and overscroll effects are created by inputting
scroll speed and distance, respectively, into custom
critically-damped harmonic oscillator
equations.
Notably, MomentaMouse is a
system. It works well
with other touch-style-scrollers as well as elements that
depend on click events. By default, it works well with
commonly used elements such as anchors, buttons, and text
boxes, but it can easily be customized to work with any
element that it needs to share "pointerdown" events with.
For example, there are interactive elements on this site, such
as links and video galleries, that are descendants of the main
MomentaMouse scroller and which also need to respond to
pointer events. These pointer event conflicts are resolved
using threshold tests with the goal of creating an intuitive
experience for the user. Nested MomentaMouse Scrollers work
perfectly well together.
SmoothScroller — SmoothScroller provides advanced,
easy-to-use smooth scrolling functionality. It allows you to
customize smooth scrolling properties, such as duration and
easing. Additionally, it includes advanced features such as
scroll events and promises.
It has a
repository
.
Every smooth scroll on this site is accomplished with this
implementation.
Below is a demonstration of SmoothScroller along with controls
for adjusting the easing, duration, x-destination, and
y-destination.
Although the easing selector in this demo only includes five
options, the scroll method's easing argument accepts any valid
set of control points (e.g. [ P1x, P1y,
P2x, P2y ]), along with the standard
keywords you may use when writing CSS transitions and
animations (e.g. "ease", "ease-in", "linear", etc.).
The results of the returned promise will appear once the
scroll has completed.
Note that if MomentaMouse is enabled, this SmoothScroller demo
will simultaneously function as a MomentaMouse demo, allowing
you to test their interactions.
The image used as the background in this demonstration is
The Scream
by Edvard Munch.
Smooth Scroller Demo
Easing
Linear
Ease-In
Ease
Ease-Out
Ease-In-Out
X
-
Y
-
Elapsed Time
-
Interrupted
-
Whereas a momentum scroll's position over time is calculated
by solving kinematic equations, a smooth scroll's position
over time is calculated by solving cubic parametric equations.
This is where WebKit's cubic Bézier root-finding algorithm
comes in.
I supply the algorithm with control points and a time ratio
and it returns a scroll ratio, which I then convert to a
scroll position.
I created this implementation because the standard method for
producing a smooth scroll
Element.scrollTo({
behavior: "smooth",
top: 200,
})
produces inconsistent results across browsers in terms of
duration and easing, is
not supported by Safari
, and — most
importantly — does not tell you if the
scroll made it to the intended destination (e.g. top: 200 in
the above example).
Update: There is a
draft
that should fix this problem if it becomes standard.
Unlike an instant scroll, a smooth scroll can be interrupted.
Your code cannot assume that a smooth scroll actually made it
to the intended destination. Some parts of my code depend on
knowing the outcome of a smooth scroll, and this information
is provided by the promise that is returned.
Responsive Video Galleries — All video galleries are aware of their
location relative to the viewport, as well as their location
relative to other video galleries, and they use this data to
intelligently decide when to play, stop, or scroll content.
Section Scroller — At the top of the portfolio is the
section scroller. It emerges from the slogan during the
loading animation sequence, and it is used for quickly
navigating the portfolio. It also automatically scrolls itself
when necessary to keep in sync with the main content.
Original Logos — Based on artwork by Charlotte Ma,
these original logos blink and talk when you tap them.
Festive, seasonal logo themes automatically appear during
certain times of the year.
October
🎃
The current kitteh is
and the current theme
is . By clicking
and dragging the selectors below, you can switch between Charm
and Shelby and the various themes.
Kitteh Logo and Theme Demo
Kitteh
Charm
Shelby
Theme
None
Auto
October
Halloween
November
December
I also created SVGs in the shape of cat paws that serve as the
grab and grabbing cursors while the momentum scrolling system
and selectors are in use.
Original Opening Animation Sequence — The logo, slogan, section scroller,
and other elements have been choreographed to create a
delightful opening animation sequence.
Other animations, such as the ripple effect on buttons and
links, and the transition between the sun and moon icons, are
original code, but were inspired by other designs.
Type and Talk System — This typewriter-style messaging system
hearkens to the old-school dialogue systems of 80s and 90s
video games.
Messages are typed, character-by-character, inside a bubble
below the logo. If an audio source is provided, it will play
as the message is typed. The syntax is:
TypeAndTalk.submitMessage(message, options)
The message parameter expects a string, which may also include
commands within the string. The {blink} and {longblink}
commands make the logo do a normal or long blink,
respectively. The {0–5000} command delays typing for the
next 0–5000 milliseconds.
The options parameter, if supplied, allows setting the
audioSource, delayStart, delayEnd, and delayBetweenChars
properties.
If an audioSource is set, it will play while the message is
typing. However, the audio will be muted until the user
unmutes the sound by tapping the sound button inside the
message bubble.
The delayStart and delayEnd options allow you to delay the
beginning of typing and the clearing away of the message,
respectively, by a number of milliseconds. The arguments must
be a number between 0 and 5000. Numbers outside this range
will be adjusted.
The delayBetweenChars option expects a number between 0 and
120, which sets the number of milliseconds delay between typed
characters.
Message Typing Demo
Message typing is paused while menu is open
Longer delays are automatically added after punctuation,
unless overridden with an intrastring delay command, and a
message is automatically cleared from the screen shortly after
typing has finished, unless another message replaces it
sooner.
If the “···” menu is opened while a message is
on screen, the message will fade out, and typing and audio
will pause. The message will fade in and typing and audio will
resume once the “···” menu is closed.
A message's status (untyped, typing, typing (paused), typed)
is updated as necessary, and the promise returned by the
submitMessage method resolves once the message has finished
typing.
Want to speed up the typing? You can! Just tap and hold the
message. This is a behavior commonly implemented in video
games with lots of dialogue.
Dark and Light Modes — On first visit, the site's appearance
is determined by the device's mode, light or dark, if
available. If not, the appearance defaults to light. The
appearance may be toggled between light and dark, and the
preferred setting is remembered for future visits.
Keyboard Accessibility — All interactive elements are keyboard
accessible. Simply tab to an element and press the "Enter" key
to interact with it.
Elements intuitively gain or lose focus when necessary
depending on site circumstances.
Cancellable Actions — If the "Escape" key is pressed while
holding the "Enter" key or while holding the pointer on an
interactive element, the behavior that would have been
triggered is canceled.
Additionally, if you tap and hold on an interactive element,
and, while holding, move the pointer outside of the target and
let go, the action will be canceled.
Except for selectors, actions are only executed if the
"pointerdown" and "pointerup" targets match. Selectors are
canceled only if, while the selector is being held, the
"Escape" key is pressed or the pointer goes beyond the edge of
a touch-screen.
This is Charm's Jobs. It and
are my first web projects.
my portfolio
The Charm's Jobs app is a progressive web app. It will be the
home for the upcoming book series “Charm's Jobs.”
As with my portfolio, everything in Charm's Jobs is made from
scratch with basic tools — HTML, CSS, and
JavaScript. No libraries. No frameworks.
The Charm's Jobs logo and the artwork for the Sardine theme are
by Charlotte Ma. The font,
Comic Neue
, is by Craig Rozynski, and the icons,
Material Icons
, are by Google.
Charm's Jobs is fully compatible with the Blink and Gecko
rendering engines. Work on WebKit compatibility is ongoing.
Features
Responsive Design — The book displays one or two pages
depending on the book container's aspect
ratio — not simply the device's
orientation.
Although the book container's aspect ratio is influenced by
the device's orientation, it is also influenced by the height
of the bottom navigation bar, which may be in a raised or
lowered position.
Works Offline — Books can be read offline as long as
they have been downloaded. The Charm's Jobs app leverages the
service worker API and cache storage API, along with a custom
file versioning system, to efficiently manage cache.
Every time a book is loaded, the app checks that all required
resources are downloaded and current. Outdated resources are
automatically purged. Just one HTTP request is required to
check the freshness of all resources.
Touch Gestures and Keyboard Shortcuts — Navigating Charm's Jobs is intuitive.
Custom touch gestures and keyboard shortcuts are available to
turn pages, control audio playback, and adjust volume.
Saved Preferences — User preferences, such as the
last-used theme (including time of day and weather for the
Sardine theme), volume level, auto-play, and bottom navigation
bar position, are saved for future visits.
Auto Resume — The last page that a user viewed is
remembered so that the book will automatically scroll to that
page the next time the book is opened.
Real Progress Bars — Progress bars are shown when book
resources are being loaded or downloaded and they reflect the
actual progress of the data being downloaded to the device.
Sardine Theme Features
Dynamic Backgrounds — When the time of day is set to
current, the background will become a dynamic one, updating
itself every minute. When a specific time of
day — dawn, sunrise, morning, afternoon,
sunset, dusk, or night — is chosen, the
background will become static after transitioning to that time
of day.
Twinkling stars and a cat-themed-constellation start to appear
around sunset. They reach their greatest visibility at night,
and then gradually fade away as sunrise approaches.
Weather — When the weather is set to calm, the
sky becomes clear and the waves gently sway back and forth.
When set to storm, the sky gradually darkens, lightning begins
to flash, and the waves become choppier.
Animated Buoy — The brightness of the buoy's lights
varies with the time of day. The position of the reflections
off the surface of the sea take into account the position of
the lights.