A simple drawing of a briefcase, bisected diagonally, with one side a light-brown color and the other side a black color. The face of a black cat with yellow eyes is partially visible as it peeks around the brown edge of the diagonal bisection. This is the logo for the Hide n' Seek browser extension.
Portfolio website logo featuring Charm, a black cat with yellow eyes, and the letters 'dmg' above his head
Charm's Jobs website logo featuring Charm, a black cat with yellow eyes and a confident smile
You Can Verify
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.
A promotional marquee for the Hide n' Seek browser extension. The Hide n' Seek logo and name are featured on the left side of the marquee. The logo is a simple drawing of a briefcase, bisected diagonally, with one side a light-brown color and the other side a black color. The face of a black cat with yellow eyes is partially visible as it peeks around the brown edge of the diagonal bisection. A before and after demonstration of how Hide n' Seek hides jobs is featured on the right side of the marquee, titled: View the jobs you seek. Hide the ones you don't
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.
A promotional marquee for the Hide n' Seek browser extension. It demonstrates how the popup may be used to unhide all jobs.
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.
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
This site is my portfolio. It and are my first web projects.
The is available on GitHub.
Originally, my plan was to create a facsimile of and publish it to the web. To accomplish that plan, I started teaching myself HTML, CSS, and JavaScript. , 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 , 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, and , are by Steve Matteson and Craig Rozynski, respectively. The icons, except for the sun-moon transition icon, are 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 .
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.
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 and a .
MomentaMouse: A momentum scrolling system for mouse-users
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
Elapsed Time
The generation of momentum scrolls is simply an application of . 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 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 .
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 by Edvard Munch.
Smooth Scroller Demo
Start Scrolling
Elapsed Time
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
  behavior: "smooth",
  top: 200,
produces inconsistent results across browsers in terms of duration and easing, is , 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 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.
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
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.
Charm's and Shelby's paws serve as the grab and grabbing cursors
Original Opening Animation Sequence — The logo, slogan, section scroller, and other elements have been choreographed to create a delightful opening animation sequence.
An original 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
Submit Message
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.
Charm's Jobs Sardine theme background varies with the time of day
This is Charm's Jobs. It and are my first web projects.
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, , is by Craig Rozynski, and the icons, , are by Google.
Charm's Jobs is fully compatible with the Blink and Gecko rendering engines. Work on WebKit compatibility is ongoing.
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.
The University of Texas at Austin College of Pharmacy
Achieved proficiency in data cleaning, exploratory data analysis, data visualization, statistical methodologies, model building, and machine learning
Texas State Board of Pharmacy
American Heart Association
American Pharmacists Association
The University of Texas at Austin College of Pharmacy
American Pharmacists Association
Software Developer
I truly love what I do, and I believe it shows in my work. I'm an innovative and entrepreneurial software developer whose top priority has always been the user experience. Nothing else is more important.
I know how to build things from the ground up. My portfolio includes numerous high-quality software solutions, all of which I conceived and developed independently, from initial concept to final product.
Hide n' Seek — An award-winning, highly-rated browser extension for Chrome and Edge. Awarded the “Featured” badge by the Chrome Web Store for following best practices in coding, design, user experience, privacy, and performance.
MomentaMouse — An innovative, user-centered front-end plugin that utilizes principles of kinematics and damped harmonic oscillators to give mouse-users the ability to navigate websites with delightfully tactile flicking gestures.
SmoothScroller — A user-centered front-end plugin that leverages Bézier curves and asynchrony to achieve adaptable, visually stunning smooth scrolls.
Personal Website — A unique, responsive, and accessible personal website that features completely original designs, logic, logos, vector artwork, animations, and more. Written in HTML, CSS, and JavaScript.
Charm's Jobs — A responsive and beautifully animated audiobook progressive web app that works online and offline.
Repositories — 
General — Problem Solving, Versatility, Creativity, Teamwork, Communication, Empathy, Leadership
Programming — Computer Science, Physics, Mathematics, Object-Oriented Programming, Agile, Scrum, CRUD, REST, Git, CSS, HTML, JavaScript, TypeScript, C#, Ruby, SQL, Node.js, MongoDB
Design — User Experience, User Interface, Animation, Photoshop, Illustrator, Premiere Pro, After Effects, Boxy SVG, SketchUp
Data Science — Machine Learning, Model Building, Statistical Analysis, Exploratory Data Analysis, Data Visualization, Python, NumPy, pandas, seaborn, matplotlib, Jupyter Notebook, Excel, Tableau, Power BI
AI Training Specialist
Data Annotation Tech
Meticulously refines and fine-tunes generative AI models that specialize in code production, using prompt engineering, comprehensive code review, and comparative assessment of response quality
Captioning Agent
Exceeded customer expectations by captioning phone conversations to written form with 97% accuracy for a high-volume call center
Pharmacist Intern (Ambulatory Care)
University Health
Lead patient interviews in a pharmacist-run clinic
Optimized medication regimens in patients with uncontrolled diabetes, hypertension, and dyslipidemia
Promoted behavioral strategies to help patients quit tobacco and recommended tobacco-cessation pharmacotherapy
Pharmacist Intern (Poison Control)
South Texas Poison Center
Provided initial and follow-up toxicological management consultation to inpatient and outpatient healthcare providers, as well as to the general public
Pharmacist Intern (Internal Medicine)
Audie L. Murphy Memorial VA Hospital
Optimized patient care by providing daily guideline-based and protocol-directed pharmacy consultation to the internal medicine team
Provided comprehensive counseling to patients being discharged with anticoagulant prescriptions
Critically evaluated an article published in the Annals of Pharmacotherapy and emailed the corresponding author after discovering a major statistical error
Pharmacist Intern (Hospital Pharmacy)
University Health
Prepared and verified the accuracy of compounded sterile preparations, including IV admixtures and TPNs
Clarified medication orders and provided guideline-based and protocol-directed recommendations to prescribers
Operated Pyxis automated dispensing cabinets
Pharmacist Intern (Pharmacy Management)
Partnered with Aetna at sales meetings to promote H-E-B pharmacy services to Medicare Part D beneficiaries
Provided MTM services using MirixaPro and OutcomesMTM to H-E-B pharmacy customers
Provided immunization services at two flu clinics
Pharmacist Intern (Psychiatric Pharmacy)
San Antonio State Hospital
Performed mental status exams and recommended adjustments to antipsychotic medication regimens for patients with severe cases of schizophrenia, bipolar disorder, and other mental illnesses
Assessed for antipsychotic-induced movement disorders and made treatment recommendations to attending psychiatrists
Pharmacist Intern (Community Pharmacy)
Prevented a potentially significant medication error involving diclofenac gel
Helped a patient identify a medication that was causing intolerable lower extremity edema
Served as the primary immunizer while on duty
Performed prescription transfers, drug utilization review, verification, counseling, annual inventory, recordkeeping, and more, while under preceptor supervision
Pharmacy Technician